Загрузка данных
import random
import time
from machine import Pin, SPI, UART, PWM, time_pulse_us
import bluetooth
from ble_simple_peripheral import BLESimplePeripheral
import dht
# ==============================================================================
# 1. ДРАЙВЕР ДИСПЛЕЯ ILI9341 (320x240)
# ==============================================================================
class ILI9341Direct:
def __init__(self, spi, cs_pin, dc_pin, rst_pin):
self.spi = spi
self.cs = Pin(cs_pin, Pin.OUT)
self.dc = Pin(dc_pin, Pin.OUT)
self.rst = Pin(rst_pin, Pin.OUT)
self.reset()
self.init_display()
def reset(self):
self.rst.value(0)
time.sleep_ms(50)
self.rst.value(1)
time.sleep_ms(100)
def write_cmd(self, cmd):
self.dc.value(0)
self.cs.value(0)
self.spi.write(bytearray([cmd]))
self.cs.value(1)
def write_data(self, data):
self.dc.value(1)
self.cs.value(0)
self.spi.write(data)
self.cs.value(1)
def init_display(self):
self.write_cmd(0x01)
time.sleep_ms(100)
self.write_cmd(0x11)
time.sleep_ms(120)
self.write_cmd(0x3A)
self.write_data(b'\x55')
self.write_cmd(0x36)
self.write_data(b'\x28')
self.write_cmd(0x29)
time.sleep_ms(50)
def set_window(self, x0, y0, x1, y1):
self.write_cmd(0x2A)
self.write_data(bytes([(x0 >> 8) & 0xFF, x0 & 0xFF, (x1 >> 8) & 0xFF, x1 & 0xFF]))
self.write_cmd(0x2B)
self.write_data(bytes([(y0 >> 8) & 0xFF, y0 & 0xFF, (y1 >> 8) & 0xFF, y1 & 0xFF]))
self.write_cmd(0x2C)
def clear(self, color=0x0000):
self.set_window(0, 0, 319, 239)
row_buf = color.to_bytes(2, 'big') * 320
for _ in range(240):
self.write_data(row_buf)
# ==============================================================================
# 2. СВЕРХБЫСТРЫЕ ДЕКОДЕРЫ VIPER
# ==============================================================================
@micropython.viper
def decode_chunk_normal(raw_ptr: ptr8, rgb_ptr: ptr8, num_bytes: int, fg_h: int, fg_l: int, bg_h: int, bg_l: int):
out_idx = 0
for i in range(num_bytes):
b = raw_ptr[i]
for bit in range(7, -1, -1):
if (b >> bit) & 1:
rgb_ptr[out_idx] = fg_h
rgb_ptr[out_idx + 1] = fg_l
else:
rgb_ptr[out_idx] = bg_h
rgb_ptr[out_idx + 1] = bg_l
out_idx += 2
@micropython.viper
def decode_chunk_mirrored(raw_ptr: ptr8, rgb_ptr: ptr8, num_bytes: int, bytes_per_row: int, fg_h: int, fg_l: int, bg_h: int, bg_l: int):
num_rows = num_bytes // bytes_per_row
out_idx = 0
for r in range(num_rows):
row_start = r * bytes_per_row
for c in range(bytes_per_row - 1, -1, -1):
b = raw_ptr[row_start + c]
for bit in range(8):
if (b >> bit) & 1:
rgb_ptr[out_idx] = fg_h
rgb_ptr[out_idx + 1] = fg_l
else:
rgb_ptr[out_idx] = bg_h
rgb_ptr[out_idx + 1] = bg_l
out_idx += 2
def play_animation(tft, filename, width=240, height=240, fps=30, fg_color=0xFFFF, bg_color=0x0000, mirror=False):
bytes_per_row = width // 8
frame_bytes = bytes_per_row * height
raw_buf = bytearray(frame_bytes)
chunk_rows = 20
chunk_raw_bytes = bytes_per_row * chunk_rows
chunk_rgb = bytearray(width * chunk_rows * 2)
fg_h = (fg_color >> 8) & 0xFF
fg_l = fg_color & 0xFF
bg_h = (bg_color >> 8) & 0xFF
bg_l = bg_color & 0xFF
x_offset = (320 - width) // 2
y_offset = (240 - height) // 2
delay_ms = int(1000 / fps) if fps > 0 else 0
try:
with open(filename, 'rb') as f:
while True:
read_bytes = f.readinto(raw_buf)
if read_bytes < frame_bytes:
break
t_start = time.ticks_ms()
tft.set_window(x_offset, y_offset, x_offset + width - 1, y_offset + height - 1)
for offset in range(0, frame_bytes, chunk_raw_bytes):
chunk_mem = memoryview(raw_buf)[offset : offset + chunk_raw_bytes]
if mirror:
decode_chunk_mirrored(chunk_mem, chunk_rgb, chunk_raw_bytes, bytes_per_row, fg_h, fg_l, bg_h, bg_l)
else:
decode_chunk_normal(chunk_mem, chunk_rgb, chunk_raw_bytes, fg_h, fg_l, bg_h, bg_l)
tft.write_data(chunk_rgb)
if delay_ms > 0:
elapsed = time.ticks_diff(time.ticks_ms(), t_start)
sleep_time = delay_ms - elapsed
if sleep_time > 0:
time.sleep_ms(sleep_time)
except OSError:
print(f'Файл {filename} не найден!')
# ==============================================================================
# 3. НАСТРОЙКА ПИНОВ И УСТРОЙСТВ
# ==============================================================================
# Дисплей
spi = SPI(2, baudrate=40000000, sck=Pin(18), mosi=Pin(23), miso=Pin(19))
tft = ILI9341Direct(spi, cs_pin=5, dc_pin=2, rst_pin=4)
tft.clear(0x0000)
# Сервопривод (Шея)
servo = PWM(Pin(25), freq=50)
# Датчик DHT11
dht_sensor = dht.DHT11(Pin(33))
# Ультразвук (Trig=16, Echo=35)
trig = Pin(16, Pin.OUT)
echo = Pin(35, Pin.IN)
# Моторы (L9110S)
M_A_1A = Pin(26, Pin.OUT)
M_A_1B = Pin(27, Pin.OUT)
M_B_1A = Pin(13, Pin.OUT)
M_B_1B = Pin(14, Pin.OUT)
# Bluetooth (BLE)
ble = bluetooth.BLE()
sp = BLESimplePeripheral(ble, name="LOIK 2 PRO")
# ==============================================================================
# 4. ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
# ==============================================================================
def set_servo_angle(angle):
duty = int(4000 + (angle / 180) * 4000)
servo.duty_u16(duty)
def measure_distance():
trig.value(0)
time.sleep_us(2)
trig.value(1)
time.sleep_us(10)
trig.value(0)
try:
duration = time_pulse_us(echo, 1, 30000)
if duration < 0:
return 999
return (duration * 0.0343) / 2
except OSError:
return 999
def stop_motors():
M_A_1A.value(0); M_A_1B.value(0)
M_B_1A.value(0); M_B_1B.value(0)
def move_forward():
M_A_1A.value(1); M_A_1B.value(0)
M_B_1A.value(1); M_B_1B.value(0)
def move_backward():
M_A_1A.value(0); M_A_1B.value(1)
M_B_1A.value(0); M_B_1B.value(1)
def turn_left():
M_A_1A.value(0); M_A_1B.value(1)
M_B_1A.value(1); M_B_1B.value(0)
def turn_right():
M_A_1A.value(1); M_A_1B.value(0)
M_B_1A.value(0); M_B_1B.value(1)
def OG():
rand_val = random.randint(5, 20)
print(f"Проверка бездействия. Выпало число: {rand_val}")
if rand_val >= 14:
print("Скучно! Осматриваемся...")
set_servo_angle(180); time.sleep(1.5)
set_servo_angle(0); time.sleep(3)
set_servo_angle(90); time.sleep(1)
# ==============================================================================
# 5. ОБРАБОТКА КОМАНД BLUETOOTH
# ==============================================================================
def on_rx(data):
try:
cmd = data.decode('utf-8').strip().lower()
print(f'Команда по BT: {cmd}')
if cmd == 'love':
play_animation(tft, 'anim_1bit6.bin', fps=30)
elif cmd == 'transform':
use_mirror = random.choice([True, False])
play_animation(tft, 'transform.bin', fps=30, mirror=use_mirror)
elif cmd == 'left':
turn_left(); time.sleep(0.5); stop_motors()
elif cmd == 'right':
turn_right(); time.sleep(0.5); stop_motors()
elif cmd == 'forward':
move_forward(); time.sleep(1); stop_motors()
elif cmd == 'back':
move_backward(); time.sleep(1); stop_motors()
except Exception as e:
print("Ошибка команды:", e)
sp.on_write(on_rx)
# ==============================================================================
# 6. ОСНОВНОЙ ЦИКЛ
# ==============================================================================
print("Робот запущен! Режим автономии активирован.")
play_animation(tft, 'blink.bin', width=240, height=240, fps=30)
last_action_time = time.ticks_ms()
current_action = 'stop'
action_end_time = time.ticks_ms()
last_distance = 999
last_dht_time = time.ticks_ms()
while True:
now = time.ticks_ms()
# 1. Чтение DHT11 раз в 3 секунды
if time.ticks_diff(now, last_dht_time) > 3000:
try:
dht_sensor.measure()
print(f"Температура: {dht_sensor.temperature()}C, Влажность: {dht_sensor.humidity()}%")
except OSError:
pass
last_dht_time = now
# 2. Проверка "Края стола" (если датчик видит резкое увеличение расстояния)
dist = measure_distance()
if last_distance < 10 and dist > 20: # Если было близко, а стало далеко
print("КРАЙ СТОЛА! Едем назад.")
move_backward()
time.sleep(1.5)
turn_right()
time.sleep(0.8)
stop_motors()
action_end_time = time.ticks_ms() # Сбрасываем таймер действий
last_distance = dist
# 3. Логика случайного движения (если не заняты другими делами)
if time.ticks_diff(now, action_end_time) > 0:
stop_motors()
# Выбираем случайное действие
action = random.choice(['forward', 'forward', 'forward', 'left', 'right'])
duration = random.uniform(1.0, 2.5) # от 1 до 2.5 секунд
print(f"Случайное действие: {action} на {duration:.1f} сек")
if action == 'forward':
move_forward()
elif action == 'left':
turn_left()
elif action == 'right':
turn_right()
action_end_time = time.ticks_add(now, int(duration * 1000))
last_action_time = now
# 4. Раз в 10 секунд проверяем, не пора ли осмотреться
if time.ticks_diff(now, last_action_time) > 10000:
OG()
last_action_time = time.ticks_ms()
# Задержка, чтобы ESP32 не зависала
time.sleep_ms(50)