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


PYTHON CODE:
import os
import serial
import tkinter as tk
from PIL import Image, ImageTk

# --- КОНФИГУРАЦИЯ ПОДКЛЮЧЕНИЯ ---
PORT = 'COM3'  # Замените на ваш актуальный порт
BAUD_RATE = 9600

try:
    ser = serial.Serial(PORT, BAUD_RATE, timeout=0.01)
except Exception as e:
    ser = None
    print(f"Внимание: Не удалось подключиться к {PORT}. Запуск в демо-режиме. Ошибка: {e}")

# --- ФУНКЦИОНАЛЬНАЯ ЛОГИКА ---
def send(cmd):
    """Отправка символьных команд в Arduino."""
    if ser and ser.is_open:
        ser.write(cmd.encode())
    else:
        print(f"[Демо] Отправлена команда: {cmd}")

def update_dist():
    """Чтение данных датчика из COM-порта в реальном времени."""
    if ser and ser.is_open:
        while ser.in_waiting:
            try:
                line = ser.readline().decode().strip()
                if line.startswith("DIST:"):
                    # Выделяем число после двоеточия
                    d = line.split(":")[1]
                    dist_label.config(text=f"{d} см")
            except Exception:
                pass
    root.after(50, update_dist)

def set_manual():
    """Переключение в ручной режим."""
    send('M')
    status_label.config(text="Режим: Ручной", fg="#2980b9")

def set_auto():
    """Переключение в автоматический режим."""
    send('A')
    status_label.config(text="Режим: Автомат", fg="#27ae60")

# --- СОЗДАНИЕ ИНТЕРФЕЙСА ---
root = tk.Tk()
root.title("Управление дроном")
root.geometry("360 rounded")
root.geometry("380x620")
root.configure(bg="#f8f9fa")

# =========================================================================
#  МЕСТО ДЛЯ ДОБАВЛЕНИЯ ФОТО ДРОНА
#  Положите изображение 'drone.png' в папку со скриптом. Размер подгонится сам.
# =========================================================================
IMAGE_PATH = 'drone.png'
if os.path.exists(IMAGE_PATH):
    try:
        img = Image.open(IMAGE_PATH).resize((220, 140), Image.Resampling.LANCZOS)
        photo = ImageTk.PhotoImage(img)
        img_label = tk.Label(root, image=photo, bg="#f8f9fa")
        img_label.pack(pady=(15, 5))
    except Exception:
        pass
else:
    # Заглушка, если фото не найдено
    placeholder = tk.Label(root, text="[ Добавьте 'drone.png' в папку проекта ]", 
                           font=("Arial", 9, "italic"), fg="#7f8c8d", bg="#f8f9fa")
    placeholder.pack(pady=(20, 10))
# =========================================================================

# --- БЛОК ТЕЛЕМЕТРИИ (ДАТЧИК) ---
tk.Label(root, text="Расстояние до препятствия", font=("Arial", 10), fg="#7f8c8d", bg="#f8f9fa").pack()
dist_label = tk.Label(root, text="0 см", font=("Arial", 24, "bold"), fg="#2c3e50", bg="#f8f9fa")
dist_label.pack(pady=(0, 10))

# --- БЛОК ВЫБОРА РЕЖИМА ---
mode_frame = tk.Frame(root, bg="#f8f9fa")
mode_frame.pack(pady=5)

tk.Button(mode_frame, text="Ручной", font=("Arial", 10, "bold"), bg="#ebf5fb", fg="#2980b9",
          command=set_manual, width=12, height=1, relief="groove").pack(side="left", padx=8)

tk.Button(mode_frame, text="Автомат", font=("Arial", 10, "bold"), bg="#e8f8f5", fg="#27ae60",
          command=set_auto, width=12, height=1, relief="groove").pack(side="left", padx=8)

status_label = tk.Label(root, text="Режим: Ручной", font=("Arial", 11, "italic"), fg="#2980b9", bg="#f8f9fa")
status_label.pack(pady=(5, 15))

# --- ДЖОЙСТИК УПРАВЛЕНИЯ ---
btn_frame = tk.Frame(root, bg="#f8f9fa")
btn_frame.pack(pady=10)

# Стилизация навигационных кнопок
btn_style = {"font": ("Arial", 10, "bold"), "bg": "#ffffff", "fg="#34495e", "width": 9, "height": 2, "relief": "raised"}

# Сетка кнопок (Исправлена накладка кнопок Назад/Вниз)
tk.Button(btn_frame, text="Вперёд", command=lambda: send('F'), **btn_style).grid(row=0, column=1, padx=6, pady=6)
tk.Button(btn_frame, text="Влево",  command=lambda: send('L'), **btn_style).grid(row=1, column=0, padx=6, pady=6)
tk.Button(btn_frame, text="Вверх",  command=lambda: send('U'), **btn_style).grid(row=1, column=1, padx=6, pady=6)
tk.Button(btn_frame, text="Вправо", command=lambda: send('R'), **btn_style).grid(row=1, column=2, padx=6, pady=6)
tk.Button(btn_frame, text="Назад",  command=lambda: send('B'), **btn_style).grid(row=2, column=1, padx=6, pady=6)
tk.Button(btn_frame, text="Вниз",   command=lambda: send('D'), **btn_style).grid(row=3, column=1, padx=6, pady=6)

# --- ЗАПУСК ПОТОКОВ ---
root.after(100, update_dist)
root.mainloop()


ARDUINO IDE CODE:

#include <Servo.h>

// --- НАСТРОЙКА ПИНОВ И ПЕРИФЕРИИ ---
Servo servoYaw;
Servo servoPitch;

const int motorPin = 5;
const int trigPin = 12;
const int echoPin = 13;

// --- ПЕРЕМЕННЫЕ СОСТОЯНИЯ ---
int motorSpeed = 150;
int yaw = 90;
int pitch = 90;
char mode = 'M';

void setup() {
  Serial.begin(9600);
  
  // Инициализация сервоприводов и пинов
  servoYaw.attach(9);
  servoPitch.attach(6);
  pinMode(motorPin, OUTPUT);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  
  // Начальное положение и запуск мотора
  servoYaw.write(yaw);
  servoPitch.write(pitch);
  analogWrite(motorPin, motorSpeed);
}

void loop() {
  // 1. Измерение расстояния ультразвуковым датчиком
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  
  long duration = pulseIn(echoPin, HIGH);
  int dist = constrain(duration * 0.034 / 2, 0, 150);
  
  // Отправка телеметрии в Python-приложение
  Serial.print("DIST:");
  Serial.println(dist);

  // 2. Обработка команд от Python (Смена режимов и ручное управление)
  if (Serial.available()) {
    char cmd = Serial.read();
    
    // Смена режима работы дрона
    if (cmd == 'M') mode = 'M';
    if (cmd == 'A') mode = 'A';
    
    // ЛОГИКА РУЧНОГО РЕЖИМА ('M')
    if (mode == 'M') {
      switch (cmd) {
        case 'U': motorSpeed = min(255, motorSpeed + 20); break; // Вверх (Газ +)
        case 'D': motorSpeed = max(0,   motorSpeed - 20); break; // Вниз (Газ -)
        case 'L': yaw        = max(0,   yaw - 15);        break; // Влево
        case 'R': yaw        = min(180, yaw + 15);        break; // Вправо
        case 'F': pitch      = min(180, pitch + 15);      break; // Вперед (Тангаж)
        case 'B': pitch      = max(0,   pitch - 15);      break; // Назад (Тангаж)
      }
      // Применение ручных команд к исполнительным механизмам
      servoYaw.write(yaw);
      servoPitch.write(pitch);
      analogWrite(motorPin, motorSpeed);
    }
  }

  // 3. ЛОГИКА АВТОМАТИЧЕСКОГО РЕЖИМА ('A')
  if (mode == 'A') {
    if (dist < 30) {
      // Обход препятствия (ближе 30 см)
      analogWrite(motorPin, 200);
      servoYaw.write(120);
      servoPitch.write(110);
    } else {
      // Стабильный полет (путь свободен)
      analogWrite(motorPin, 150);
      servoYaw.write(90);
      servoPitch.write(90);
    }
  }

  delay(50); // Частота работы цикла 20 Гц
}