USB HID (Human Interface Device) sınıfından bir donanım üzerinden bilgisayarın klavye ve fare arayüzünü AI ile kontrol etmek için geliştirilmiş açık kaynak proje.
ESP32-S2 Lolin Mini'yi USB klavye + fare olarak kullanan firmware, CRC32 doğrulamalı seri protokol ve Python client kütüphanesi. Yapay zeka ajanlarının fiziksel bilgisayarları gerçek HID cihazı üzerinden otonom olarak kullanmasını sağlar.
Anahtar Kelimeler: USB HID, Human Interface Device, ESP32-S2, AI computer use, keyboard emulator, mouse emulator, hardware automation, keystroke injection, TinyUSB, Arduino, Python serial, CRC32 protocol, Turkish keyboard layout, TR-Q, ISO/ANSI, device spoofing, VID/PID, penetration testing, red team, physical access, USB rubber ducky alternative, BadUSB, HID attack, automation framework, ESP32 keyboard mouse, USB gadget, human typing simulation
Kartı bilgisayarına USB ile takıyorsun. Bilgisayar bunu gerçek bir klavye ve fare olarak görüyor. Sonra Python ile komut gönderiyorsun — kart o komutları klavye/fare hareketi olarak bilgisayara iletiyor.
Kısa versiyon:
from client.aihid_client import AIHIDClient with AIHIDClient("/dev/tty.usbmodem1101") as hid: hid.text("Merhaba Dünya!") # bilgisayara "Merhaba Dünya!" yazar hid.combo("KEY_LEFT_CTRL", "c") # Ctrl+C basar hid.mouse(100, 0) # fareyi 100px sağa hareket ettirir hid.click("L") # sol tıklama yapar
esp_ai_hid/
├── aihid_firmware/ ← Arduino sketch (IDE uyumlu)
│ ├── aihid_firmware.ino ← ESP32 firmware
│ ├── BOARD_CONFIG.md ← Flash layout, NVS, pin detayları
│ └── data/
│ └── EspTinyUSB-master.zip
├── client/
│ ├── aihid_client.py ← Python client kütüphanesi
│ └── demos/
│ └── safari_search.py ← Safari + Google arama demosu
├── tests/
│ ├── conftest.py ← pytest ayarları + marker'lar
│ └── test_aihid.py ← Test paketi (72 unit + 22 integration)
├── requirements.txt
└── README.md ← Bu dosya
- 1x Lolin S2 Mini (ESP32-S2 tabanlı)
- 1x USB-C kablo
- Bilgisayar (macOS, Windows, Linux)
-
Arduino IDE 2.x indir ve kur.
-
ESP32 board paketini ekle:
File → Preferences → Additional Board Manager URLskısmına yapıştır:https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.jsonOKtıkla.
-
Board paketini kur:
Tools → Board → Boards Manageraçesp32ara- espressif olanını kur (sürüm 3.x)
-
Board ayarlarını yap (
Toolsmenüsü):Ayar Değer Board LOLIN S2 Mini USB CDC On Boot Disabled USB Mode USB-OTG (TinyUSB) Core Debug Level None USB DFU On Boot Disabled Erase All Flash Before Upload Disabled USB Firmware MSC On Boot Disabled Partition Scheme Default 4MB with ffat (1.2MB APP/1.5MB FATFS) Upload Speed 921600 CPU Frequency 240MHz USB CDC On Boot = Disabled çok önemli. Enabled olursa USB stack kodundan önce başlar ve VID/PID/isim değiştirme çalışmaz. Bunu yanlış ayarlaman en sık karşılaşılan sorun.
Detaylı flash layout, NVS şeması ve pin bilgileri:
aihid_firmware/BOARD_CONFIG.md
aihid_firmware/aihid_firmware.inodosyasını Arduino IDE'de aç.- Kartı USB ile bilgisayara bağla.
- Port'u seç:
Tools → Port → (kartın portu). - Upload butonuna bas.
- Eğer kart otomatik boot moduna girmezse:
0butonuna basılı tutRSTbutonuna bas0butonunu bırak- Tekrar Upload'a bas
- Yükleme bittikten sonra USB kablosunu çıkar.
- Farklı bir USB portuna tekrar tak.
- Yeni bir serial port oluşacak — o portu not et.
İlk yüklemeden sonra USB'yi çıkarıp takmak ZORUNLU. Çünkü USB descriptor'lar sadece boot sırasında yükleniyor.
macOS:
system_profiler SPUSBDataType | grep -A5 "AI"
Şunu görmelisin:
AI Keyboard + Mouse:
Manufacturer: AI Controller
USB Vendor ID: 0x303a
USB Product ID: 0x00ab01
Linux:
lsusb | grep 303aWindows: Aygıt Yöneticisi → USB cihazları
pip install pyserial
Başka bir şey gerekmez. client/aihid_client.py tek dosya, bağımlılığı sadece pyserial.
get_mouse_position() ve move_to(from_corner=False) kullanacaksan:
# X11 (Arch / CachyOS) sudo pacman -S xdotool # X11 (Ubuntu / Debian) sudo apt install xdotool # Wayland — KDE sudo pacman -S kdotool # veya: pip install kdotool # Wayland — Genel (sway, GNOME, vb.) sudo pacman -S ydotool
xdotool/kdotool/ydotoolyüklü değilsemove_to()otomatik olarak köşe reset'e (from_corner=True) düşer — pozisyon algılama aracı gerekmez. Yalnızcafrom_corner=Falsemodunda hızlı delta hareketi için gereklidir.Algılama zinciri: X11 →
xdotool| Wayland →kdotool→ydotool| macOS →Quartz| Windows →GetCursorPosEkran çözünürlüğü algılama (
get_screen_resolution()) X11'dexrandr, Wayland'daswaymsgkullanır — bunlar genelde önyüklüdür.
from client.aihid_client import AIHIDClient # Otomatik port tespiti (VID/PID ile cihazı bulur) hid = AIHIDClient() # veya manuel port hid = AIHIDClient("/dev/tty.usbmodem1101") # İşin bitince kapat hid.disconnect()
Veya with bloğu ile (önerilen):
with AIHIDClient("/dev/tty.usbmodem1101") as hid: hid.ping() # ... işlemler ... # Blok bitince otomatik kapanır
macOS:
ls /dev/tty.usbmodem*Linux:
ls /dev/ttyACM*Windows:
Aygıt Yöneticisi → Bağlantı Noktaları (COM ve LPT) → COMxx
hid.text("Merhaba Dünya!")
Bilgisayarda imleç neredeyse oraya yazar. Türkçe karakterler (ğ, ü, ş, ı, ö, ç ve büyük halleri) desteklenir.
Yeni satır:
hid.text("Satır 1\\nSatır 2\\nSatır 3") # veya hid.text("Satır 1{ENTER}Satır 2{ENTER}Satır 3")
Tab:
hid.text("Sütun1\\tSütun2\\tSütun3") # veya hid.text("Sütun1{TAB}Sütun2{TAB}Sütun3")
Yazı arasında bekleme:
hid.text("kullanici@email.com{TAB}{DELAY:500}SuperSifre123{ENTER}")
Bu ne yapar:
- E-posta yazar
- Tab'a basar (şifre alanına geçer)
- 500ms bekler (sayfa animasyonu için)
- Şifreyi yazar
- Enter'a basar
{DELAY:ms} — milisaniye cinsinden bekleme. Max 30000 (30 saniye). Negatif değer 0'a clamp edilir.
TEXT içinde tuş basımı:
# Yanlış yazdın, düzelt hid.text("yanlsi{K:KEY_BACKSPACE:2}ış") # yanlış → "yanlış" (2 karakter siler, doğrusunu yazar) # Ok tuşlarıyla gezin hid.text("metin{K:KEY_LEFT:3}ek") # "metin" yaz → 3 sola git → "ek" yaz → "meetkinn" değil "meetkinn" # Tek tuş hid.text("satır1{K:KEY_RETURN}satır2") # Escape tuşu hid.text("{K:KEY_ESCAPE}")
{K:TUŞ_ADI} — tek basım. {K:TUŞ_ADI:N} — N kere tekrar (max 255). Tüm tuş isimleri (bkz. Bölüm 12) desteklenir.
| Escape | Açıklama |
|---|---|
{K:KEY_BACKSPACE} |
1x Backspace |
{K:KEY_BACKSPACE:5} |
5x Backspace |
{K:KEY_LEFT:3} |
3x Sol ok |
{K:KEY_DELETE} |
1x Delete |
{K:KEY_HOME} |
Satır başı |
{K:KEY_END} |
Satır sonu |
{K:KEY_ESCAPE} |
ESC |
{K:KEY_F5} |
F5 (yenile) |
{K:KEY_RETURN} |
Enter (= {ENTER}) |
{K:KEY_UP:10} |
10x Yukarı ok |
Backslash yazmak:
hid.text("C:\\\\Users\\\\Desktop") # Python'da \\\\ gerekli
hid.key("A") hid.key("5") hid.key("ğ")
hid.press("KEY_RETURN") # Enter hid.press("KEY_TAB") # Tab hid.press("KEY_BACKSPACE") # Backspace hid.press("KEY_ESCAPE") # Escape hid.press("KEY_F5") # F5 hid.press("KEY_DELETE") # Delete hid.press("KEY_UP") # Yukarı ok hid.press("KEY_DOWN") # Aşağı ok hid.press("KEY_LEFT") # Sol ok hid.press("KEY_RIGHT") # Sağ ok hid.press("KEY_HOME") # Home hid.press("KEY_END") # End
hid.combo("KEY_LEFT_CTRL", "c") # Ctrl+C (kopyala) hid.combo("KEY_LEFT_CTRL", "v") # Ctrl+V (yapıştır) hid.combo("KEY_LEFT_CTRL", "a") # Ctrl+A (tümünü seç) hid.combo("KEY_LEFT_CTRL", "z") # Ctrl+Z (geri al) hid.combo("KEY_LEFT_ALT", "KEY_TAB") # Alt+Tab (pencere değiştir) hid.combo("KEY_LEFT_ALT", "KEY_F4") # Alt+F4 (pencereyi kapat) hid.combo("KEY_LEFT_GUI", "KEY_SPACE") # Cmd+Space (macOS Spotlight) hid.combo("KEY_LEFT_CTRL", "KEY_LEFT_SHIFT", "KEY_ESCAPE") # Ctrl+Shift+Esc (Görev Yöneticisi)
KEY_LEFT_GUI = Windows tuşu / macOS Command tuşu.
hid.hold("KEY_LEFT_SHIFT") # Shift'i basılı tut hid.press("KEY_RIGHT") # Sağa git (seçim yapar) hid.press("KEY_RIGHT") hid.press("KEY_RIGHT") hid.release("KEY_LEFT_SHIFT") # Shift'i bırak hid.combo("KEY_LEFT_CTRL", "c") # Seçili metni kopyala
hid.mouse(100, 0) # 100 piksel sağa hid.mouse(-100, 0) # 100 piksel sola hid.mouse(0, 100) # 100 piksel aşağı hid.mouse(0, -100) # 100 piksel yukarı hid.mouse(50, 50) # çapraz (sağ-aşağı)
İlk sayı = yatay (+ sağ, - sol). İkinci sayı = dikey (+ aşağı, - yukarı). Max ±10000 piksel. Büyük hareketler otomatik küçük adımlara bölünür.
hid.click("L") # Sol tıklama hid.click("R") # Sağ tıklama hid.click("M") # Orta tıklama (scroll tuşu)
hid.dblclick("L") # Sol çift tıklama hid.dblclick("R") # Sağ çift tıklama
İki tıklama arasında 60-100ms rastgele gecikme — gerçek insan gibi görünür.
hid.wheel(3) # 3 birim aşağı scroll hid.wheel(-3) # 3 birim yukarı scroll
Max ±127.
Göreceli sürükleme — mevcut konumdan:
hid.drag(200, 0) # 200px sağa sürükle hid.drag(0, 100) # 100px aşağı sürükle hid.drag(-100, -50) # sol-yukarı sürükle
Sol tuşu basar → hareket eder → bırakır.
İki nokta arası sürükle-bırak:
hid.dragto(100, 200, 400, 200) # (100,200) noktasına git → sol tuş bas → (400,200)'e sürükle → bırak
Manuel sürükleme bırakma:
hid.dragrel() # Yarım kalan sürüklemeyi bırak
hid.release() # HER ŞEYİ bırak (klavye + fare) hid.release("KB") # Sadece klavye hid.release("MOUSE") # Sadece fare hid.release("L") # Sol fare butonu hid.release("R") # Sağ fare butonu hid.release("M") # Orta fare butonu hid.release("KEY_LEFT_SHIFT") # Tek tuş
Bir şeyler sıkıştıysa (tuş basılı kaldı, fare takıldı):
hid.release()çağır. Her şeyi sıfırlar.
Fareyi ekran üzerinde belirli bir koordinata veya köşeye götürür. Herhangi bir çözünürlükte (8K/16K dahil) çalışır.
from client.aihid_client import get_screen_resolution, get_mouse_position # Ekran çözünürlüğü ve fare pozisyonu algıla w, h = get_screen_resolution() # (3840, 2160) x, y = get_mouse_position() # (512, 384) # Köşeye git hid.move_to_corner("top-left") # Sol üst (0, 0) hid.move_to_corner("bottom-right") # Sağ alt # Mutlak koordinata git hid.move_to(960, 540) # Önce köşeye reset, sonra hedefe hid.move_to(1200, 800, from_corner=False) # Mevcut pozisyondan delta (hızlı) # Pozisyon takibi print(hid.mouse_pos) # (1200, 800) print(hid.screen_size) # (3840, 2160)
HumanMouseConfig ile WindMouse veya Bézier algoritmasıyla gerçek insana benzeyen fare hareketi:
from client.aihid_client import HumanMouseConfig # Varsayılan — WindMouse, hızlı + insansı hid.move_to(960, 540, humanize=True) # Tam kontrol cfg = HumanMouseConfig( algorithm="wind", # "wind" veya "bezier" speed=1.8, # Hız çarpanı (0.5=yavaş, 3.0=hızlı) gravity=12.0, # Hedefe çekim (yüksek=hızlı yakınsama) wind=4.0, # Rastgele sapma (doğal eğri) overshoot_threshold=300, # Bu mesafeden büyükse overshoot yap overshoot_radius=0.03, # Overshoot mesafesi (oransal) jitter=0.8, # El titremesi (piksel) ) hid.move_to(1500, 900, from_corner=False, human_config=cfg)
Algoritmalar:
| Algoritma | Açıklama | En İyi Kullanım |
|---|---|---|
wind |
WindMouse: fizik tabanlı (yerçekimi + rüzgar). Doğal overshoot, sapma, yavaşlama. | Genel kullanım, en gerçekçi |
bezier |
Kübik Bézier eğrisi + easing. Öngörülebilir, yumuşak hareket. | Kısa mesafe, UI etkileşimi |
Hız profilleri:
# Yavaş ve çok doğal cfg = HumanMouseConfig(speed=0.5, jitter=1.5) # Varsayılan — hızlı ama insansı cfg = HumanMouseConfig() # speed=1.8 # Çok hızlı — robot/insan arası cfg = HumanMouseConfig(speed=3.0, jitter=0.3, overshoot_radius=0) # Bézier — yumuşak eğri cfg = HumanMouseConfig(algorithm="bezier", easing="ease-in-out", speed=1.2)
Ekran sınırı koruması: Hiçbir parametre kombinasyonu fareyi ekran dışına çıkaramaz. Tüm ara adımlar ekran sınırlarına kırpılır.
Tüm ayarlar cihazın kalıcı belleğine (NVS) kaydedilir. Güç kesilse bile hatırlanır.
hid.set_lang("TR") # Türkçe-Q layout (varsayılan) hid.set_lang("US") # US English layout
Anında geçerli olur, reboot gerekmez.
Önemli: Bilgisayarında da aynı layout seçili olmalı. macOS:
System Settings → Keyboard → Input Sources → Turkish-Q
hid.set_kb("ISO") # macOS (varsayılan) hid.set_kb("ANSI") # Windows/Linux
macOS ISO klavyelerde " ve < tuşları yer değiştirir. macOS kullanıyorsan ISO'da bırak.
hid.set_human(True) # İnsansı gecikme açık (varsayılan) hid.set_human(False) # Robot hızında yaz
Açıkken her tuş arası rastgele gecikme eklenir. Doğal görünür.
Gecikme ayarları:
# Normal harf arası: min-max ms hid.set_typing_delay(30, 120) # Noktalama/boşluk sonrası: min-max ms hid.set_punctuation_delay(200, 500)
Profil örnekleri:
# Yavaş — daktilo hissi hid.set_typing_delay(80, 200) hid.set_punctuation_delay(400, 800) # Hızlı — ama yine insansı hid.set_typing_delay(10, 35) hid.set_punctuation_delay(50, 150) # Robot — en hızlı hid.set_human(False)
Bilgisayar USB cihazını bu bilgilerle tanır. Değiştirebilirsin:
hid.set_vid(0x046D) # Vendor ID (örn: Logitech) hid.set_pid(0xC31C) # Product ID hid.set_manufacturer("Logitech") # Üretici ismi hid.set_product("Logitech Keyboard") # Ürün ismi hid.set_serial("ABC123") # Seri numarası hid.reboot() # DEĞİŞİKLİKLER REBOOT SONRASI GEÇERLİ!
Reboot zorunlu. USB descriptor'lar sadece boot sırasında yükleniyor.
Host OS cache: Bilgisayar eski VID/PID'yi hatırlayabilir. Farklı USB portuna takmayı dene.
Linux uyarı: Logitech Unifying Receiver PID'si (
0xC52B) kullanmayın. Linuxhid-logitech-djkernel modülü bu PID'yi yakalar ve özel Logitech protokolü bekler — cihaz HID olarak çalışmaz. Bunun yerine0xC31C(K120 Keyboard) gibi genel HID sürücüsüne düşen bir PID tercih edin.
hid.set("LANG", "TR") # Yukarıdaki fonksiyonların alt seviye hali hid.set("TYPMIN", "50")
if hid.ping(): print("Cihaz bağlı ve çalışıyor") else: print("Cihaz yanıt vermiyor")
info = hid.getinfo() print(info["VID"]) # "0x303A" print(info["PID"]) # "0xAB01" print(info["MANU"]) # "AI Controller" print(info["LANG"]) # "TR" print(info["HEAP"]) # "180000" (kullanılabilir RAM, byte) print(info["FW"]) # "v3.0" print(info["PROTO"]) # "1"
Dönen tüm alanlar:
| Alan | Açıklama |
|---|---|
| VID | USB Vendor ID |
| PID | USB Product ID |
| MANU | Manufacturer ismi |
| PROD | Product ismi |
| SER | Serial numarası |
| LANG | Klavye layout (TR/US) |
| KB | Klavye tipi (ISO/ANSI) |
| HUMAN | İnsansı yazım (ON/OFF) |
| TYMIN | Tuş gecikme min (ms) |
| TYMX | Tuş gecikme max (ms) |
| TYPN | Noktalama gecikme min (ms) |
| TYPX | Noktalama gecikme max (ms) |
| DRAG | Sürükleme aktif mi (YES/NO) |
| FW | Firmware sürümü |
| PROTO | Protokol sürümü |
| HEAP | Boş RAM (byte) |
| UP | Çalışma süresi (saniye) |
commands = hid.help() # ['TEXT', 'KEY', 'PRESS', 'COMBO', 'HOLD', 'RELEASE', 'MOUSE', ...]
hid.reboot()
USB bağlantısı kopar. Tekrar bağlanmak için:
hid.reboot() time.sleep(3) hid.disconnect() hid = AIHIDClient("/dev/tty.usbmodem1101") hid.wait_for_boot()
Client yazmak isteyenler için. Python dışında bir dilde client yazacaksan bu bölümü oku.
- Fiziksel: USB CDC (sanal serial port), 115200 baud
- Encoding: UTF-8
- Satır sonu:
\n(LF) - Her komut için tam olarak bir response satırı döner
Komut + CRC suffix, \n ile biter:
KOMUT:parametre\x04CRCHEX\n
\x04(EOT) = CRC ayırıcıCRCHEX= komutun CRC32'si (8 hex, uppercase)- CRC, komutun kendisi (
KOMUT:parametre) üzerinden hesaplanır (suffix hariç) - Cihaz CRC'yi doğrular: uyuşmazlıkta
ERR rx_crc_mismatchdöner - CRC suffix opsiyonel — yoksa cihaz eski protokolle (tek yönlü) çalışır
>CRCHEX OK [payload]
>CRCHEX ERR reason
>— response satırı başlangıcıCRCHEX— gönderilen komutun CRC32'si, 8 hex karakter, uppercaseOK— komut başarılıERR— komut başarısız,reason= İngilizce hata açıklamasıpayload— komuta özel ek bilgi (opsiyonel, virgülle ayrılmış key=value)
#TAG message
#ile başlar, CRC yok- Client bunları bilgilendirme olarak loglamalı
- Bilinen tag'ler:
#BOOT— cihaz açıldı, firmware bilgileri#WARN— uyarı (heap düşük, satır kesildi vb.)
Standart CRC32 (IEEE 802.3), komut string'inin UTF-8 byte'ları üzerinde hesaplanır.
Python:
import binascii crc = binascii.crc32(command.encode('utf-8')) & 0xFFFFFFFF
C:
#include <esp_rom_crc.h> uint32_t crc = esp_rom_crc32_le(0, (const uint8_t*)cmd, len);
- Serial'den satır oku
>ile başlıyorsa → response:- İlk 8 hex karakteri çıkar → response CRC
- Gönderdiğin komutun CRC'si ile karşılaştır
- Eşleşmiyorsa → CRC hata
OKveyaERRoku- Payload'ı parse et
#ile başlıyorsa → sistem mesajı, logla- Diğer → görmezden gel
[cihaz açılır]
← #BOOT AI_HID v3.0 PROTO=1 VID=0x303A PID=0xAB01 LANG=TR KB=ISO
→ PING
← >B7B2364B OK PONG
→ TEXT:Merhaba
← >A3F1C2D4 OK bytes=7
→ WHEEL:abc
← >C5B6A7D8 ERR invalid_number
→ SET:VID=0x046D
← >E1D2F3A4 OK VID=0x046D,reboot_required
→ GETINFO
← >D4C3B2A1 OK VID=0x303A,PID=0xAB01,MANU=AI Controller,...
→ REBOOT
← >F1E2D3C4 OK rebooting
[bağlantı kopar]
| Reason | Açıklama |
|---|---|
empty |
Parametre boş (KEY:, PRESS:, HOLD:) |
invalid_char |
Geçersiz/tanınamayan karakter |
invalid_number |
Sayı parse edilemedi |
invalid_button 'X' (L/R/M) |
Geçersiz fare butonu |
invalid_value TR|US |
Geçersiz değer, kabul edilenler listesi |
unknown_key KEY_XYZ |
Bilinmeyen tuş ismi |
unknown_command BLABLA |
Bilinmeyen komut |
unknown_param XYZ |
Bilinmeyen SET parametresi |
unknown_target XYZ |
Bilinmeyen RELEASE hedefi |
empty_value |
SET değeri boş |
too_long max=63 |
String çok uzun |
format MOUSE:dx,dy |
Yanlış parametre formatı |
unsupported U+XXXX |
Desteklenmeyen Unicode karakter |
cmd_too_long len=N max=512 |
Komut çok uzun |
no_valid_keys |
COMBO'da geçerli tuş yok |
heap_low N |
Yetersiz bellek, komut reddedildi |
rx_crc_mismatch |
Komut iletimde bozuldu (bidirectional CRC) |
pip install pyserial pytest
MockSerial ile firmware davranışını simüle eder. 72 test:
python -m pytest tests/ -v -k unit
Gerçek cihaza komut gönderir, response doğrular. 22 test. Port iki yöntemle belirtilebilir:
# Yöntem 1: --port argümanı python -m pytest tests/ -v -k integration --port /dev/tty.usbmodem1101 # Yöntem 2: PORT ortam değişkeni PORT=/dev/tty.usbmodem1101 python -m pytest tests/ -v -k integration
Dikkat:
test_text_*testleri gerçekten yazı yazar. İmleç neredeyse oraya yazılır. Bir metin editörü aç ve imleci oraya bırak.
python -m pytest tests/ -v --port /dev/tty.usbmodem1101
Manuel test ve debug için:
python tests/test_aihid.py /dev/tty.usbmodem1101
with AIHIDClient("/dev/tty.usbmodem1101") as hid: hid.click("L") # username alanına tıkla hid.text("kullanici@email.com{TAB}{DELAY:300}Sifre123{ENTER}")
with AIHIDClient("/dev/tty.usbmodem1101") as hid: hid.combo("KEY_LEFT_CTRL", "a") # Tümünü seç hid.combo("KEY_LEFT_CTRL", "c") # Kopyala hid.combo("KEY_LEFT_ALT", "KEY_TAB") # Sonraki pencere time.sleep(0.5) hid.combo("KEY_LEFT_CTRL", "v") # Yapıştır
with AIHIDClient("/dev/tty.usbmodem1101") as hid: hid.combo("KEY_LEFT_GUI", "KEY_SPACE") # Spotlight aç time.sleep(0.5) hid.text("Terminal") hid.press("KEY_RETURN")
with AIHIDClient("/dev/tty.usbmodem1101") as hid: hid.press("KEY_HOME") # Satır başı hid.hold("KEY_LEFT_SHIFT") # Shift basılı hid.press("KEY_END") # Satır sonu (seçer) hid.release("KEY_LEFT_SHIFT") # Shift bırak hid.press("KEY_DELETE") # Sil
with AIHIDClient("/dev/tty.usbmodem1101") as hid: hid.set_vid(0x046D) hid.set_pid(0xC31C) hid.set_manufacturer("Logitech") hid.set_product("Logitech Keyboard") hid.set_serial("LGT2025") hid.reboot()
with AIHIDClient("/dev/tty.usbmodem1101") as hid: hid.set_human(True) hid.set_typing_delay(10, 35) hid.set_punctuation_delay(50, 150) hid.text("Bu metin hızlı ama insansı yazılacak.")
from aihid_client import AIHIDClient with AIHIDClient("/dev/tty.usbmodem1101") as hid: if not hid.ping(): print("FAIL: ping") exit(1) info = hid.getinfo() heap = int(info.get("HEAP", "0")) print(f"FW: {info['FW']}") print(f"PROTO: {info['PROTO']}") print(f"HEAP: {heap} bytes") print(f"UPTIME: {info['UP']}s") if heap < 50000: print("WARNING: low heap") else: print("OK")
Python client üç exception fırlatabilir:
from aihid_client import AIHIDError, AIHIDTimeout, AIHIDCRCMismatch try: hid.send("WHEEL:abc") except AIHIDError as e: print(f"Cihaz hata döndü: {e.reason}") # e.reason = "invalid_number" # e.command = "WHEEL:abc" # e.crc = 0xABCD1234 except AIHIDTimeout: print("Cihaz yanıt vermedi") except AIHIDCRCMismatch as e: print(f"CRC uyuşmazlığı: beklenen {e.expected:08X}, gelen {e.got:08X}") # Veri bozulmuş, komutu tekrar gönder
Firmware'de 10 saniyelik watchdog çalışır. Herhangi bir sebepten kod donarsa (sonsuz döngü, USB stack hatası), watchdog cihazı otomatik yeniden başlatır.
Normal kullanımda bir şey yapman gerekmez. Uzun TEXT yazımlarında ve büyük fare hareketlerinde watchdog otomatik beslenir.
USB CDC On Boot→ Disabled mı? (En sık yapılan hata)- Kartı çıkar, farklı USB portuna tak.
- Yeni oluşan portu seç.
- Baud rate: 115200.
hid.set_lang("TR")gönder.- Bilgisayarın klavye düzeni Turkish-Q mi kontrol et.
"ve<yer değişiyorsa:hid.set_kb("ISO")(macOS) veyahid.set_kb("ANSI")(Windows/Linux).
USB CDC On Boot→ Disabled olmalı.- SET sonrası
hid.reboot()çağırdın mı? - Farklı USB portuna taktın mı? (OS cache)
hid.getinfo()ile kontrol et.
- 10 saniye bekle — watchdog otomatik yeniden başlatır.
- USB'yi çıkar-tak.
- Hâlâ yanıt yoksa: boot moduna girip yeniden flash'la.
- USB kablosu bozuk olabilir — değiştir.
- Baud rate doğru mu (115200)?
- Başka bir program aynı portu kullanıyor olabilir.
- Port doğru mu?
- Cihaz çalışıyor mu? (LED yanıyor mu?)
- Timeout süresini artır:
AIHIDClient(port, timeout=10.0) hid.ping()ile test et.
| İsim | Tuş |
|---|---|
KEY_RETURN / KEY_ENTER |
Enter |
KEY_ESC / KEY_ESCAPE |
Escape |
KEY_BACKSPACE |
Backspace |
KEY_TAB |
Tab |
KEY_SPACE |
Boşluk |
KEY_CAPS_LOCK |
Caps Lock |
KEY_F1 – KEY_F12 |
Fonksiyon tuşları |
KEY_PRINT_SCREEN |
Print Screen |
KEY_INSERT |
Insert |
KEY_DELETE |
Delete |
KEY_HOME |
Home |
KEY_END |
End |
KEY_PAGE_UP |
Page Up |
KEY_PAGE_DOWN |
Page Down |
KEY_UP / KEY_UP_ARROW |
Yukarı ok |
KEY_DOWN / KEY_DOWN_ARROW |
Aşağı ok |
KEY_LEFT / KEY_LEFT_ARROW |
Sol ok |
KEY_RIGHT / KEY_RIGHT_ARROW |
Sağ ok |
KEY_LEFT_CTRL |
Sol Ctrl |
KEY_LEFT_SHIFT |
Sol Shift |
KEY_LEFT_ALT |
Sol Alt |
KEY_LEFT_GUI |
Sol Win / Cmd |
KEY_RIGHT_CTRL |
Sağ Ctrl |
KEY_RIGHT_SHIFT |
Sağ Shift |
KEY_RIGHT_ALT |
Sağ Alt (AltGr) |
KEY_RIGHT_GUI |
Sağ Win / Cmd |
KEY_NUM_LOCK |
Num Lock |
KEY_SCROLL_LOCK |
Scroll Lock |
| Parametre | Min | Max | Aşılırsa |
|---|---|---|---|
{DELAY:ms} |
0 | 30000 | Clamp edilir |
{K:KEY:N} tekrar |
1 | 255 | Clamp edilir |
MOUSE / DRAG koordinat |
-10000 | 10000 | Clamp edilir |
WHEEL |
-127 | 127 | Clamp edilir |
SET:TYPMIN/TYPMAX |
0 | 5000 | Clamp edilir |
SET:TYPOMIN/TYPOMAX |
0 | 10000 | Clamp edilir |
SET:MANU/PROD/SERIAL |
1 | 63 karakter | ERR too_long |
TEXT: komutu |
- | 4096 byte | Kesilir + WARN |
| Diğer komutlar | - | 512 byte | ERR cmd_too_long |
Negatif sayılar: sayısal alanlarda negatif değer 0'a clamp edilir (gecikme, VID/PID). Mouse/wheel'da negatif geçerlidir (yön belirtir).
TEXT filtrelenen byte'lar:
\x00(NUL),\x01(SOH),\x02(STX),\x04(EOT) byte'ları TEXT içeriğinden sessizce temizlenir. Bunlar firmware'in dahili sentinel/CRC ayırıcı byte'larıdır. 4-byte UTF-8 dizileri (emoji, U+10000+) desteklenmez ve atlanır.
- MCU: ESP32-S2 (Xtensa LX7, 240MHz, 320KB SRAM)
- USB: Native USB-OTG, TinyUSB stack
- HID:
pressRaw()ile doğrudan HID scancode (kütüphane ASCII bypass) - NVS: Preferences kütüphanesi ile kalıcı ayar saklama
- Watchdog:
esp_task_wdt, 10s timeout, panic reboot - CRC:
esp_rom_crc32_le(IEEE 802.3 CRC32) - TR-Q Map: 95 ASCII + 13 Unicode karakter, ISO/ANSI swap
- Human Typing:
esp_random()tabanlı, karakter sınıfına göre değişken gecikme - Buffer: Safe serial read, hard byte limit, UTF-8/escape-aware tail trim
- Heap Guard: 8KB altında komut ERR ile reddedilir (CRC korunur)
- TEXT Escapes:
\n,\t,\\,{ENTER},{TAB},{DELAY:ms},{K:KEY:N} - TEXT Chunking: Client 3900 byte'lık parçalara böler (UTF-8/escape aware)
- Partition: 4MB flash, ffat (1.2MB APP + 1.5MB FATFS), OTA destekli
Bu proje aşağıdaki açık kaynak kütüphaneler ve araçlar üzerine inşa edilmiştir:
| Kütüphane / Araç | Açıklama | Link |
|---|---|---|
| ESP32 Arduino Core | ESP32 ailesi için Arduino framework | github.com/espressif/arduino-esp32 |
| TinyUSB | ESP32-S2 native USB-OTG stack | github.com/hathach/tinyusb |
| EspTinyUSB (chegewara) | ESP32-S2/S3 USB HID kütüphanesi | github.com/chegewara/EspTinyUSB |
| USBHIDKeyboard / USBHIDMouse | Arduino ESP32 USB HID kütüphaneleri | ESP32 Arduino Core içinde |
| Preferences | ESP32 NVS (Non-Volatile Storage) sarmalayıcı | ESP32 Arduino Core içinde |
| pyserial | Python serial port kütüphanesi | github.com/pyserial/pyserial |
| pytest | Python test framework | github.com/pytest-dev/pytest |
| Arduino IDE | Geliştirme ortamı ve derleyici | arduino.cc |
| Espressif IDF | ESP32 alt seviye SDK (ROM CRC32, WDT) | github.com/espressif/esp-idf |
Donanım: Wemos/Lolin S2 Mini — ESP32-S2 tabanlı geliştirme kartı.
AI Desteği: Kod geliştirme, güvenlik auditi ve dokümantasyon sürecinde Claude (Anthropic) kullanılmıştır.
Copyright 2026 Burak Boz
Bu proje Creative Commons Attribution 4.0 International (CC BY 4.0) lisansı ile lisanslanmıştır.
Özgürce kullanabilir, değiştirebilir ve dağıtabilirsiniz — tek koşul kaynak belirtmektir.