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


from machine import Pin, I2C
import ssd1306
import time
import math

# ==========================================
# # НАСТРОЙКИ
# ==========================================
OLED_WIDTH = 128
OLED_HEIGHT = 64
OLED_ADDRESS = 0x3C

START_TEXT = "Najimai lubuiu knopky"
TEXT_BUTTON_1 = "a/(sin a)=b/(sin b)=c/(sin c)=2R|a^2=b^2+c^2-2bc*cosa"

DEBOUNCE_MS = 50

# ==========================================
# # ИНИЦИАЛИЗАЦИЯ OLED И КНОПОК
# ==========================================
i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000)
oled = ssd1306.SSD1306_I2C(OLED_WIDTH, OLED_HEIGHT, i2c, addr=OLED_ADDRESS)

button_pins = [
    Pin(13, Pin.IN, Pin.PULL_UP),
    Pin(14, Pin.IN, Pin.PULL_UP),
    Pin(25, Pin.IN, Pin.PULL_UP),
    Pin(26, Pin.IN, Pin.PULL_UP)
]

last_raw_states = [button.value() for button in button_pins]
stable_states = last_raw_states[:]
change_times = [time.ticks_ms()] * len(button_pins)

# ==========================================
# # ДАННЫЕ ДЛЯ КНОПКИ 2 (ЛИЦО КОТИКА)
# ==========================================
# Массив байт изображения котика (64x64 пикселя)
CAT_AVATAR = bytes([
    0x00, 0x00, 0x03, 0xC0, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x0F, 0xF0, 0x00, 0x00,
    0x00, 0x00, 0x3F, 0xFC, 0x3F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
    0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00,
    0x00, 0x3F, 0xFE, 0x0F, 0xF0, 0x7F, 0xFC, 0x00, 0x00, 0x7F, 0xF8, 0x03, 0xC0, 0x1F, 0xFE, 0x00,
    0x00, 0xFF, 0xE0, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x01, 0xFF, 0x80, 0x00, 0x00, 0x01, 0xFF, 0x80,
    0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x07, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xE0,
    0x0F, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x1F, 0xF8, 0x0F, 0x00, 0x00, 0xF0, 0x1F, 0xF8,
    0x1F, 0xF0, 0x3F, 0x80, 0x01, 0xFC, 0x0F, 0xF8, 0x3F, 0xE0, 0x7F, 0xC0, 0x03, 0xFE, 0x07, 0xFC,
    0x3F, 0xE0, 0xFF, 0xE0, 0x07, 0xFF, 0x07, 0xFC, 0x7F, 0xC0, 0xFF, 0xF0, 0x0F, 0xFF, 0x03, 0xFE,
    0x7F, 0xC1, 0xFF, 0xF0, 0x0F, 0xFF, 0x83, 0xFE, 0x7F, 0x81, 0xFF, 0x30, 0x0C, 0xFF, 0x81, 0xFF,
    0xFF, 0x83, 0xFC, 0x30, 0x0C, 0x3F, 0xC1, 0xFF, 0xFF, 0x83, 0xF8, 0x00, 0x00, 0x1F, 0xC1, 0xFF,
    0xFF, 0x03, 0xF8, 0x00, 0x00, 0x1F, 0xC0, 0xFF, 0xFF, 0x03, 0xFC, 0x00, 0x00, 0x3F, 0xC0, 0xFF,
    0xFF, 0x03, 0xFF, 0x80, 0x01, 0xFF, 0xC0, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF,
    0x7F, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0xFF, 0x7F, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF,
    0x7F, 0xC0, 0x7F, 0xFF, 0xFF, 0xFE, 0x03, 0xFE, 0x3F, 0xC0, 0x1F, 0xFE, 0x7F, 0xF8, 0x03, 0xFE,
    0x3F, 0xE0, 0x07, 0xF8, 0x1F, 0xE0, 0x07, 0xFC, 0x1F, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8,
    0x1F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF8, 0x0F, 0xF8, 0x0E, 0x38, 0x1C, 0x70, 0x1F, 0xF0,
    0x07, 0xFC, 0x1F, 0x7C, 0x3E, 0xF8, 0x3F, 0xE0, 0x03, 0xFE, 0x3F, 0xFE, 0x7F, 0xFC, 0x7F, 0xC0,
    0x01, 0xFF, 0x7F, 0xDF, 0xFB, 0xFE, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xCF, 0xF3, 0xFF, 0xFF, 0x00,
    0x00, 0x7F, 0xFF, 0x80, 0x01, 0xFF, 0xFE, 0x00, 0x00, 0x3F, 0xFE, 0x03, 0xC0, 0x7F, 0xFC, 0x00,
    0x00, 0x0F, 0xFC, 0x0F, 0xF0, 0x3F, 0xF0, 0x00, 0x00, 0x03, 0xF8, 0x3F, 0xFC, 0x1F, 0xC0, 0x00,
    0x00, 0x00, 0xF0, 0x7F, 0xFE, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
    0x00, 0x05, 0x01, 0xFF, 0xFF, 0x80, 0xA0, 0x00, 0x00, 0x0F, 0x83, 0xFF, 0xFF, 0xC1, 0xF0, 0x00,
    0x00, 0x3F, 0xC7, 0xFF, 0xFF, 0xE3, 0xFC, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00,
    0x00, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0x00, 0x01, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, 0x80,
    0x03, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xE0,
    0x0F, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFE, 0x00, 0x00, 0x7F, 0xFF, 0xF8,
    0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xFF, 0xFC, 0x7F, 0xFF, 0xF8, 0x00, 0x00, 0x1F, 0xFF, 0xFE,
    0x7F, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xFE, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x07, 0xFF, 0xFF,
    0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x01, 0xFF, 0xFF,
    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF,
    0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF
])

def draw_cat():
    oled.fill(0)
    # Выводим по центру экрана (смещение X=32)
    for y in range(64):
        for x in range(8):
            byte = CAT_AVATAR[y * 8 + x]
            for bit in range(8):
                if byte & (1 << (7 - bit)):
                    oled.pixel(32 + x * 8 + bit, y, 1)
    oled.show()

# ==========================================
# # ДАННЫЕ И МАТЕМАТИКА ДЛЯ 3D КУБА (КНОПКА 4)
# ==========================================
# Вершины 3D куба (X, Y, Z)
CUBE_VERTICES = [
    [-20, -20, -20], [20, -20, -20], [20, 20, -20], [-20, 20, -20],
    [-20, -20, 20],  [20, -20, 20], ,  [-20, 20, 20]
]
# Ребра куба (какие вершины соединять линиями)
CUBE_EDGES = [
    (0,1), (1,2), (2,3), (3,0),
    (4,5), (5,6), (6,7), (7,4),
    (0,4), (1,5), (2,6), (3,7)
]

angle_x = 0.0
angle_y = 0.0
angle_z = 0.0

def draw_cube():
    global angle_x, angle_y, angle_z
    oled.fill(0)
    
    # Расчет тригонометрии углов вращения
    rad_x, rad_y, rad_z = math.radians(angle_x), math.radians(angle_y), math.radians(angle_z)
    cx, sx = math.cos(rad_x), math.sin(rad_x)
    cy, sy = math.cos(rad_y), math.sin(rad_y)
    cz, sz = math.cos(rad_z), math.sin(rad_z)
    
    projected_vertices = []
    for vertex in CUBE_VERTICES:
        # Вращение вокруг X
        y1 = vertex[1] * cx - vertex[2] * sx
        z1 = vertex[1] * sx + vertex[2] * cx
        # Вращение вокруг Y
        x2 = vertex[0] * cy + z1 * sy
        z2 = -vertex[0] * sy + z1 * cy
        # Вращение вокруг Z
        x3 = x2 * cz - y1 * sz
        y3 = x2 * sz + y1 * cz
        
        # Проекция на 2D экран с добавлением смещения в центр дисплея (64, 32)
        proj_x = int(64 + x3)
        proj_y = int(32 + y3)
        projected_vertices.append((proj_x, proj_y))
        
    # Рисуем грани куба линиями
    for edge in CUBE_EDGES:
        p1 = projected_vertices[edge[0]]
        p2 = projected_vertices[edge[1]]
        oled.line(p1[0], p1[1], p2[0], p2[1], 1)
        
    oled.show()
    # Изменяем углы для постоянного движения куба
    angle_x += 2.5
    angle_y += 3.5
    angle_z += 1.0

# ==========================================
# # ФУНКЦИИ ТЕКСТА
# ==========================================
def wrap_text(text, max_chars=16):
    words = text.split()
    lines = []
    current_line = ""
    for word in words:
        if len(word) > max_chars:
            if current_line: lines.append(current_line); current_line = ""
            for i in range(0, len(word), max_chars): lines.append(word[i:i+max_chars])
        else:
            if current_line == "": current_line = word
            elif len(current_line) + 1 + len(word) <= max_chars: current_line += " " + word
            else: lines.append(current_line); current_line = word
    if current_line: lines.append(current_line)
    return lines

def show_text(text):
    oled.fill(0)
    lines = wrap_text(text)
    for num, line in enumerate(lines[:8]):
        oled.text(line, 0, num * 8)
    oled.show()

# ==========================================
# # ЗАПУСК И ГЛАВНЫЙ ЦИКЛ
# ==========================================
oled.fill(0)
show_text(START_TEXT)
time.sleep_ms(500)

# Флаги состояний анимаций
show_time_mode = False
show_cube_mode = False
last_time_update = 0

while True:
    now = time.ticks_ms()
    
    # Опрос кнопок с защитой от дребезга
    for index, button in enumerate(button_pins):
        raw_state = button.value()
        if raw_state != last_raw_states[index]:
            last_raw_states[index] = raw_state
            change_times[index] = now
        elif time.ticks_diff(now, change_times[index]) >= DEBOUNCE_MS:
            if stable_states[index] != raw_state:
                stable_states[index] = raw_state
                
                # Кнопка нажата (LOW)
                if stable_states[index] == 0:
                    # Сбрасываем активные фоновые режимы перед переключением
                    if index != 2: show_time_mode = False
                    if index != 3: show_cube_mode = False
                    
                    if index == 0:    # 1 Кнопка
                        show_text(TEXT_BUTTON_1)
                    elif index == 1:  # 2 Кнопка
                        draw_cat()
                    elif index == 2:  # 3 Кнопка
                        show_time_mode = not show_time_mode
                        if not show_time_mode: show_text("Menu closed")
                    elif index == 3:  # 4 Кнопка
                        show_cube_mode = not show_cube_mode
                        if not show_cube_mode: show_text("Cube stopped")

    # Логика работы 3-й Кнопки (Часы)
    if show_time_mode and time.ticks_diff(now, last_time_update) >= 1000:
        last_time_update = now
        t = time.localtime()
        time_str = "   TIME MENU\n\n    {:02d}:{:02d}:{:02d}".format(t[3], t[4], t[5])
        show_text(time_str)

    # Логика работы 4-й Кнопки (3D Куб)
    if show_cube_mode:
        draw_cube()
        time.sleep_ms(10)  # Регулирует скорость вращения и плавность
        
    time.sleep_ms(5)