Загрузка данных
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)