Загрузка данных


#!/usr/bin/env python3
"""
Драйвер для управления мышью и клавиатурой через Arduino Uno на Arch Linux.
Поддерживает автокалибровку нуля, мёртвую зону и плавное ускорение.
"""

import time
import sys
import serial
from pynput.mouse import Controller as MouseController, Button
from pynput.keyboard import Controller as KeyController

# === НАСТРОЙКИ УСТРОЙСТВА ===
SERIAL_PORT = '/dev/ttyACM0'  # Если не находит, проверьте /dev/ttyUSB0
BAUD_RATE   = 115200

# === НАСТРОЙКИ ДВИЖЕНИЯ МЫШИ ===
# Для горизонтального положения (пины смотрят вниз к кнопкам):
SWAP_AXES = False   # Поменяйте на True, если горизонталь и вертикаль перепутаны
INVERT_X  = False   # Инвертировать движение влево/вправо
INVERT_Y  = True    # Инвертировать движение вверх/вниз (обычно True для экрана)

DEADZONE  = 35      # Мертвая зона стика (чтобы курсор не дрожал в покое)
MAX_SPEED = 18      # Максимальная скорость курсора при крайнем отклонении
CURVE_EXP = 1.6     # Степень нелинейности (плавное прицеливание + быстрый рывок)

mouse = MouseController()
keyboard = KeyController()

# Состояния кнопок (1 - отпущено, 0 - зажато)
prev_states = {
    'middle': 1,
    'lmb': 1,
    'rmb': 1,
    'q': 1
}

def connect_serial():
    try:
        ser = serial.Serial(SERIAL_PORT, BAUD_RATE, timeout=1)
        # Очищаем буфер после открытия порта
        ser.reset_input_buffer()
        print(f"[OK] Подключено к {SERIAL_PORT}")
        return ser
    except serial.SerialException as e:
        print(f"[ОШИБКА] Не удалось открыть порт {SERIAL_PORT}: {e}")
        print("Проверьте подключение платы и права пользователя (группа uucp).")
        sys.exit(1)

def calibrate_center(ser, samples=40):
    """Калибрует нулевое положение стика при старте."""
    print("Калибровка центра стика... Не трогайте джойстик 1 секунду.")
    x_sum = 0
    y_sum = 0
    valid = 0

    while valid < samples:
        line = ser.readline().decode('utf-8', errors='ignore').strip()
        if not line:
            continue
        parts = line.split(',')
        if len(parts) == 6:
            try:
                x_sum += int(parts[0])
                y_sum += int(parts[1])
                valid += 1
            except ValueError:
                pass

    center_x = x_sum / samples
    center_y = y_sum / samples
    print(f"[OK] Калибровка завершена! Центр: X={center_x:.1f}, Y={center_y:.1f}")
    return center_x, center_y

def handle_mouse_button(button_obj, current_state, state_key):
    global prev_states
    if current_state == 0 and prev_states[state_key] == 1:
        mouse.press(button_obj)
    elif current_state == 1 and prev_states[state_key] == 0:
        mouse.release(button_obj)
    prev_states[state_key] = current_state

def handle_keyboard_key(key_char, current_state, state_key):
    global prev_states
    if current_state == 0 and prev_states[state_key] == 1:
        keyboard.press(key_char)
    elif current_state == 1 and prev_states[state_key] == 0:
        keyboard.release(key_char)
    prev_states[state_key] = current_state

def apply_curve(val, deadzone, max_speed):
    """Вычисляет смещение с учётом мёртвой зоны и кривой ускорения."""
    abs_val = abs(val)
    if abs_val <= deadzone:
        return 0.0
    
    # Нормализуем диапазон от 0.0 до 1.0
    normalized = (abs_val - deadzone) / (512.0 - deadzone)
    normalized = min(max(normalized, 0.0), 1.0)
    
    # Экспоненциальное масштабирование скорости
    speed = (normalized ** CURVE_EXP) * max_speed
    return speed if val > 0 else -speed

def main():
    ser = connect_serial()
    time.sleep(1.5) # Ждем перезагрузку платы после Serial DTR

    center_x, center_y = calibrate_center(ser)
    print("\nДрайвер запущен! Управление:")
    print(" - Стик: Движение курсора")
    print(" - Нажатие на стик: СКМ (Колёсико)")
    print(" - Кнопка D5: ЛКМ")
    print(" - Кнопка D4: ПКМ")
    print(" - Кнопка D3: Клавиша 'Q'")
    print("Для выхода нажмите Ctrl + C\n")

    try:
        while True:
            line = ser.readline().decode('utf-8', errors='ignore').strip()
            if not line:
                continue

            parts = line.split(',')
            if len(parts) == 6:
                try:
                    raw_x        = int(parts[0])
                    raw_y        = int(parts[1])
                    stick_middle = int(parts[2])
                    btn_lmb      = int(parts[3])
                    btn_rmb      = int(parts[4])
                    btn_q        = int(parts[5])

                    # Вычисляем отклонение относительно откалиброванного центра
                    dx_raw = raw_x - center_x
                    dy_raw = raw_y - center_y

                    if SWAP_AXES:
                        dx_raw, dy_raw = dy_raw, dx_raw

                    if INVERT_X:
                        dx_raw = -dx_raw
                    if INVERT_Y:
                        dy_raw = -dy_raw

                    # Расчет движения с кривой
                    dx = int(apply_curve(dx_raw, DEADZONE, MAX_SPEED))
                    dy = int(apply_curve(dy_raw, DEADZONE, MAX_SPEED))

                    if dx != 0 or dy != 0:
                        mouse.move(dx, dy)

                    # Обработка кликов мыши
                    handle_mouse_button(Button.middle, stick_middle, 'middle')
                    handle_mouse_button(Button.left,   btn_lmb,      'lmb')
                    handle_mouse_button(Button.right,  btn_rmb,      'rmb')

                    # Обработка клавиши клавиатуры 'q'
                    handle_keyboard_key('q', btn_q, 'q')

                except ValueError:
                    pass

    except KeyboardInterrupt:
        print("\nЗавершение работы драйвера.")
    finally:
        ser.close()

if __name__ == '__main__':
    main()








/*
 * Arduino Uno - Контроллер мыши и кнопок
 *
 * Подключение джойстика:
 * VRx -> A0
 * VRy -> A1
 * SW  -> D2 (Клик стика - Средняя кнопка мыши СКМ)
 *
 * Подключение кнопок (второй вывод всех кнопок идет на GND):
 * D3  -> Клавиша Q
 * D4  -> Правая кнопка мыши (ПКМ)
 * D5  -> Левая кнопка мыши (ЛКМ)
 */

const int pinX        = A0;
const int pinY        = A1;
const int pinStickBtn = 2; // СКМ

const int pinBtnQ     = 3; // Клавиша 'Q'
const int pinBtnRMB   = 4; // ПКМ
const int pinBtnLMB   = 5; // ЛКМ

void setup() {
  // Включаем внутреннюю подтяжку к 5V (INPUT_PULLUP)
  // При нажатии на кнопку на пине будет LOW (0), при отпускании - HIGH (1)
  pinMode(pinStickBtn, INPUT_PULLUP);
  pinMode(pinBtnQ, INPUT_PULLUP);
  pinMode(pinBtnRMB, INPUT_PULLUP);
  pinMode(pinBtnLMB, INPUT_PULLUP);

  Serial.begin(115200);
}

void loop() {
  // Считываем аналоговые значения со стика (диапазон 0..1023)
  int rawX = analogRead(pinX);
  int rawY = analogRead(pinY);

  // Считываем состояния кнопок
  int stickBtn = digitalRead(pinStickBtn);
  int btnLMB   = digitalRead(pinBtnLMB);
  int btnRMB   = digitalRead(pinBtnRMB);
  int btnQ     = digitalRead(pinBtnQ);

  // Передаем данные в формате CSV: X,Y,STICK_BTN,LMB,RMB,Q
  Serial.print(rawX);
  Serial.print(",");
  Serial.print(rawY);
  Serial.print(",");
  Serial.print(stickBtn);
  Serial.print(",");
  Serial.print(btnLMB);
  Serial.print(",");
  Serial.print(btnRMB);
  Serial.print(",");
  Serial.println(btnQ);

  // Частота опроса ~100 Гц для плавного движения
  delay(10);
}