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 yaparesp_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 pyserialBaş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ırmacOS:
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("[email protected]{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 \\\\ gereklihid.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") # Endhid.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 kopyalahid.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ı scrollMax ±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ükleSol 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ırakManuel sürükleme bırakma:
hid.dragrel() # Yarım kalan sürüklemeyi bırakhid.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 layoutAnı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/LinuxmacOS 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 yazAçı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')) & 0xFFFFFFFFC:
#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 pytestMockSerial ile firmware davranışını simüle eder. 72 test:
python -m pytest tests/ -v -k unitGerç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 integrationDikkat:
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.usbmodem1101Manuel test ve debug için:
python tests/test_aihid.py /dev/tty.usbmodem1101with AIHIDClient("/dev/tty.usbmodem1101") as hid:
hid.click("L") # username alanına tıkla
hid.text("[email protected]{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ırwith 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") # Silwith 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önderFirmware'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.