Загрузка данных
# ==============================================================================
# БЛОК 8: КЛАСС УПРАВЛЕНИЯ РОБОТОМ (RobotController)
# Главный класс, связывающий всё воедино. Использует машину состояний.
# ==============================================================================
class RobotController:
"""
Ядро робота. Управляет состояниями, таймерами и взаимодействием модулей.
"""
# Определяем константы состояний
STATE_IDLE = 0 # Стоим, ждем
STATE_MOVING = 1 # Двигаемся
STATE_LOOKING = 2 # Осматриваемся
STATE_EMERGENCY = 3 # Экстренное торможение
def __init__(self):
# Инициализация аппаратных модулей
self.spi = SPI(2, baudrate=40000000, sck=Pin(18), mosi=Pin(23), miso=Pin(19))
self.display = DisplayDriver(self.spi, cs_pin=5, dc_pin=2, rst_pin=4)
self.display.clear(0x0000)
# Инициализация текстового дисплея
self.text_display = None
if TEXT_LIB_AVAILABLE:
try:
self.text_display = Display(self.spi, dc=Pin(2), cs=Pin(5), rst=Pin(4), width=320, height=240, rotation=90)
except:
pass
# Инициализация менеджеров
self.sensors = SensorManager(trig_pin=16, echo_pin=35, dht_pin=33)
self.motors = MotorDriver(a1a_pin=26, a1b_pin=27, b1a_pin=13, b1b_pin=14)
self.servo = ServoManager(pin=25)
# Переменные машины состояний
self.current_state = self.STATE_IDLE
self.state_timer = time.ticks_ms() # Таймер текущего состояния
self.action_timer = 0 # Таймер длительности движения
self.last_dht_time = time.ticks_ms()
# --- ИСПРАВЛЕНИЕ: Правильные названия переменных ---
self.wait_duration = random.randint(10, 17) * 1000 # Сколько всего ждать
self.has_looked = False # Флаг осмотра
self.has_blinked = False # Флаг моргания
self.action_type = '' # Текущее действие
def update_display_text(self, t, h):
"""Обновляет текст в нижней полосе экрана."""
if self.text_display:
self.text_display.fill_rectangle(0, 220, 320, 20, color565(0, 0, 0))
self.text_display.draw_text8x8(10, 225, f"T: {t}C H: {h}%", color565(255, 255, 255), color565(0,0,0), 0)
def show_text_on_screen(self, text):
"""Выводит произвольный текст на экран."""
if self.text_display:
self.text_display.fill_rectangle(0, 20, 320, 240, color565(0, 0, 0))
self.text_display.draw_text8x8(10, 100, text, color565(255, 255, 255), color565(0,0,0), 0)
def turn_around_180(self):
"""Разворот на 180 градусов."""
self.motors.turn_left()
time.sleep(0.7)
self.motors.stop()
def execute_emergency(self):
"""Экстренная остановка при обнаружении края."""
print("!!! КРАЙ СТОЛА !!!")
self.motors.stop()
self.turn_around_180()
self.motors.forward()
time.sleep(1.5) # Отъезжаем назад
self.motors.stop()
# Сбрасываем таймеры и запускаем новый цикл ожидания
self.start_new_wait_cycle()
def start_new_wait_cycle(self):
"""
Запускается ТОЛЬКО когда робот остановился.
Строго генерирует число и фиксирует время старта.
"""
self.current_state = self.STATE_IDLE
self.state_timer = time.ticks_ms() # Засекаем время старта
rand_val = random.randint(1, 20)
self.wait_duration = rand_val * 1000
print(f"[ОЖИДАНИЕ] Выпало число: {rand_val} (Ждем {rand_val} сек)")
self.has_looked = False
self.has_blinked = False
def run(self):
print("Робот LOIK запущен!")
self.start_new_wait_cycle()
while True:
now = time.ticks_ms()
# 1. Обновление DHT11
if time.ticks_diff(now, self.last_dht_time) > 3000:
try:
t, h = self.sensors.read_dht() # ИСПРАВЛЕНО: берем данные из sensor manager
if t is not None:
self.update_display_text(t, h)
except OSError: pass
self.last_dht_time = now
# 2. Проверка края стола (ИСПРАВЛЕНО: вызов из self.sensors)
if self.current_state != self.STATE_EMERGENCY:
if self.sensors.check_edge():
self.execute_emergency()
continue
# 3. Логика состояний
if self.current_state == self.STATE_IDLE:
elapsed = time.ticks_diff(now, self.state_timer)
# 3.1 Моргание
if elapsed >= 10000 and not self.has_blinked:
play_animation(self.display, 'BLINK.bin', fps=30) # ИСПРАВЛЕНО: self.display
self.has_blinked = True
# 3.2 Осмотр
if elapsed >= 10000 and not self.has_looked:
wait_seconds = self.wait_duration // 1000
if wait_seconds >= 14:
print(f"[ОСМОТР] Крутим шеей...")
self.servo.set_angle(180); time.sleep(2) # ИСПРАВЛЕНО: self.servo
self.servo.set_angle(0); time.sleep(4)
self.servo.set_angle(90); time.sleep(1)
self.state_timer = time.ticks_add(self.state_timer, 7000)
self.has_looked = True
# 3.3 Начало движения
if elapsed >= self.wait_duration:
action = random.choice(['forward', 'forward', 'forward', 'left', 'right'])
duration = random.uniform(1.0, 2.5)
self.action_type = action
self.action_timer = time.ticks_add(now, int(duration * 1000))
self.current_state = self.STATE_MOVING
print(f"[ДВИЖЕНИЕ] Едем {action} ({duration:.1f} сек)")
elif self.current_state == self.STATE_MOVING:
if time.ticks_diff(now, self.action_timer) >= 0:
self.motors.stop() # ИСПРАВЛЕНО
self.start_new_wait_cycle()
else:
if self.action_type == 'forward':
self.motors.forward(); time.sleep_ms(50); self.motors.stop() # ИСПРАВЛЕНО
elif self.action_type == 'left':
self.motors.turn_left(); time.sleep_ms(50); self.motors.stop() # ИСПРАВЛЕНО
elif self.action_type == 'right':
self.motors.turn_right(); time.sleep_ms(50); self.motors.stop() # ИСПРАВЛЕНО
time.sleep_ms(10)
# ==============================================================================
# БЛОК 9: ОБРАБОТКА КОМАНД BLUETOOTH
# ==============================================================================
robot = RobotController()
ble = bluetooth.BLE()
sp = BLESimplePeripheral(ble, name="LOIK")
def on_rx(data):
try:
cmd = data.decode('utf-8').strip().lower()
print(f"BT Команда: {cmd}")
# --- КОМАНДЫ ДВИЖЕНИЯ ---
if cmd == 'y':
if robot.sensors.check_edge():
robot.execute_emergency()
else:
robot.current_state = robot.STATE_MOVING
robot.action_type = 'forward'
robot.action_timer = time.ticks_add(time.ticks_ms(), 1000)
elif cmd == 'o':
robot.motors.backward(); time.sleep(1); robot.motors.stop() # ИСПРАВЛЕНО
elif cmd == 'r':
robot.motors.turn_left(); time.sleep(0.5); robot.motors.stop() # ИСПРАВЛЕНО
elif cmd == 't':
robot.motors.turn_right(); time.sleep(0.5); robot.motors.stop() # ИСПРАВЛЕНО
# --- КОМАНДЫ ШЕИ ---
elif cmd == 'gleft':
robot.servo.set_angle(180); time.sleep(1.5); robot.servo.set_angle(90) # ИСПРАВЛЕНО
elif cmd == 'gright':
robot.servo.set_angle(0); time.sleep(1.5); robot.servo.set_angle(90) # ИСПРАВЛЕНО
# --- АНИМАЦИИ ---
elif cmd == 'w':
play_animation(robot.display, 'LOVE.bin', fps=30)
elif cmd == 'q':
play_animation(robot.display, 'hello.bin', fps=30)
# --- ТЕКСТ ---
elif cmd.startswith('text:'):
text_to_show = cmd.split(':')[1]
robot.show_text_on_screen(text_to_show)
# --- МОРГАНИЕ ---
elif cmd.startswith('blink:'):
try:
count = int(cmd.split(':')[1])
for _ in range(count):
play_animation(robot.display, 'blink.bin', fps=30) # ИСПРАВЛЕНО
time.sleep(0.5)
except ValueError:
print("Ошибка: неверный формат команды моргания")
elif cmd == 'stop':
robot.motors.stop() # ИСПРАВЛЕНО
robot.start_new_wait_cycle()
except Exception as e:
print("Ошибка обработки команды:", e)
sp.on_write(on_rx)
# ==============================================================================
# БЛОК 10: ЗАПУСК ПРОГРАММЫ
# ==============================================================================
if __name__ == "__main__":
robot.run()