Одним осенним днём листал я свою библиотеку Steam, попутно размышляя, во что бы поиграть. Глаза скользили по бесконечному списку игр, палец безостановочно крутил колёсико мыши, а мозг погружался в уныние из-за невозможности сделать выбор. Предвкушение приятного времяпрепровождения за интересной игрой необратимо сменялось раздражением. Я откинулся в кресле, повернул голову на пылящийся на полке Switch и… меня осенило.
Я никогда не использовал eShop по прямому назначению. Все мои игры для консоли Nintendo были на картриджах.
Запуск каждого проекта был не просто тычком на одну из множества иконок в библиотеке, а целым ритуалом. Отворить слот на консоли -> пробежаться глазами по этикеткам на полке -> взять в руки цветастую и тактильно приятную коробку -> открыть коробочку с заинтересовавшей игрой и ощутить аромат китайского пластика -> вынуть картридж из коробки и вставить его в консоль -> насладиться приятным щелчком закрывшегося слота. Каждый этап сопровождается приятным импактом, который и на десятый раз не надоедает. И именно этого эффекта лишены цифровые копии игр. Ну а если чего-то не хватает, то это что-то всегда можно добавить самому. И так меня настигла идея создания физических картриджей для Steam.
В своих конструкторских начинаниях я вдохновляюсь отечественными инженерами. А потому сразу же после начала проекта отправился на западный YouTube в поисках готовых решений. И о чудо, мои ожидания оправдались. Один парень уже задавался схожей с моей целью. И проект у него получился крайне интересный.
В двух словах о принципах его работы: в картридж вставляется NFC-метка, считыватель сканирует метку и отправляет данные в Home Assistant, который и запускает игру. Реализация проста и действенна, однако… Как бы это сказать… Лично для меня что-то не щёлкало.
Вид невероятно красивых потрёпанных упаковок картриджей не соотносился с современным и минималистичным считывателем. Мне хотелось чего-то более тактильного и в стиле ретро. Да и зависимость от Home Assistant вызывала у меня старческое кряхтение. И вот тут-то я и окончательно осознал, что мне придётся сделать то, чего я не делал уже долгие годы. Составить план.
- Сначала нужны наброски основного дизайна
- Затем спроектировать и собрать корпус и картриджи
- В корпус впихнуть электронику
- Заняться программированием
- Подключить внешнюю инфраструктуру
- Ну и наконец насладиться результатом
И тут мой рассказ сделает небольшой шаг в сторону. Дело вот в чём. Проект получился довольно крупным. И затрагивает он несколько кардинально отличающихся друг от друга областей. Работал я над ними параллельно, но вот в стройный рассказ все они не укладываются. В связи с чем всё последующее повествование будет разбито на тематические блоки.
Наброски дизайна
Как я уже говорил выше, мне хотелось чего-то в стиле ретро. Следовательно, мой взор обратился в прошлое, и я начал изучать дизайны консолей конца 80-х - начала 90-х. Сильнее всего глаз зацепился за SNES и Sega Genesis.
Они выглядят одновременно просто и изящно. Небольшая коробочка с обтекаемыми формами и плавными линиями. Подобных простых форм достаточно легко добиться, да и с компоновкой компонентов внутри этакой консоли никаких проблем быть не должно.
Но не буду лукавить. Не одним лишь ретро дело ограничилось. Краешком глаза я засматривался и на консоли седьмого поколения. В общем, дизайн тех времён был несколько хай-тековым, и тащить его в мой проект не хотелось. Однако один из самых узнаваемых элементов Xbox 360 частично попал в финальный вариант моего проекта.
С элементами управления консолью произошла забавная ситуация. Моей целью было максимально упростить и минимизировать взаимодействие с корпусом. А наличие клавиш включения или перезапуска никак этому не способствовало. И довольно быстро я решил, что лучшие кнопки - отсутствующие кнопки. Вот так в финальном дизайне и не оказалось вообще ни одной кнопочки. За одним маленьким исключением, но о нём в главе про электронику.
С картриджами же дела обстояли несколько сложнее, чем с консолью. С одной стороны, я желал добиться аутентичности картриджа и его синергии с консолью. С другой же стороны, хотелось чего-то уникального и бросающегося в глаза.
Была идея сделать фишки-кэпсы с логотипом игры.
И фишка бы вставлялась в консоль, как монетка в игровой автомат. Отговорило меня от этой идеи отсутствие цветного принтера и непонимание, как можно механически удобно реализовать слот под фишку в консоли.
Другая мысль заключалась в создании дисков, которые бы “проигрывались” при выборе нужного диска на корпусе. Звучит странно… В общем, Jukebox.
Должен был быть схожий принцип работы. Но и эту идею я отмёл в связи с излишней сложностью механической части.
Ну а потом я присмотрелся к фигурке Монокумы перед моим монитором.
Фигурка… А это ведь отличная идея! Засунуть статуэтку по мотивам игры в витрину на картридже. Это же будет выглядеть просто обалденно! Сразу понятно, что за игру собираешься запустить. Плюс фигурка может быть абсолютно любой. Главное, чтобы в витрину-картридж влезла.
Помнится, я там что-то про аутентичность говорил… Да кому нужна эта последовательность в идеях? Правда же?
Пока писал этот блог, узнал, что компания Wilder Toys уже выпускает линейку диорам Console Heroes. И некоторые из этих диорам продаются в виде корпуса от Sega Genesis. Иронично
Корпус и картриджи
Когда дизайн был придуман, а электронные компоненты измерены, я отправился в любименький мой FreeCAD создавать модель под печать.
В прошлой главе я говорил, что вдохновение идёт от форм SNES и Sega Genesis. В модели я хотел сохранить узнаваемость четвёртого поколения консолей. Округлые края, слегка приплюснутая форма, выделяющийся слот под картридж и общий вайб ретро-консоли. Преследуя этот высокий образ идеальной консоли прошлого века, я просидел за моделированием в районе недели.
Стоит сказать, что мой опыт 3D-моделирования скромен. До этого проекта я делал всего несколько простых корпусов в виде коробок. Ну и потому в работе над чуть более сложным дизайном приходилось многому учиться и ещё большее количество вещей исправлять. Постоянно слетали привязки граней модели к плоскостям. При указании размеров я забывал про усадку пластика и создание небольших зазоров для удобного стыкования элементов. Несколько раз вносил ключевые изменения не в основную модель, а в её копию. После чего удалял копию за ненадобностью. Мои эмоции в моменты осознания не описать словами.
В общем, были и пот, и слёзы, и поминание добрым словом всех, кто связан с разработкой FreeCAD. Но, несмотря на все невзгоды, я это сделал. Модель отправилась в печать.
По итогу получился роторный телефон с мыльницей на крышке.
С картриджами работа шла гораздо веселее. Благо, что их корпус - коробка с вырезом.
Согласно моему дизайн-проекту, картридж является не только носителем данных об игре, но и витриной. Следовательно, ему нужна прозрачная стенка. Изначально стенку я планировал сделать из прозрачного пластика. Но печать даже в несколько слоёв делает пластик слишком мутным. Для рассеивателя света подходит идеально, но как прозрачная стенка абсолютно не работает. А потому я заказал тонкое оргстекло и вооружился канцелярским ножом. Результаты моего издевательства над стеклом и пластиком на скринах ниже:
Когда-то давно я пару недель увлекался шитьём игрушек из фетра. С тех пор в моих закромах остались небольшие запасы материалов. Ну вот я и подумал: а что, если сделать из фетра одну из фигурок для картриджа? Моделью для вышивки выступил один из опциональных боссов игры Chrono Ark - маленький ежонок, изображённый на иконке игры в Steam.
Ну а на фотографиях ниже - его путь от листа бумаги до полноценной фигурки внутри картриджа
На финал главы оставил немного технической информации. Картридж фиксируется в корпусе с помощью соединения “Dove Tail”. Размер корпуса - 150x65x100, а картриджа - 90x95x35. Корпус состоит из двух половинок, прилепленных друг к другу на магниты, а все электронные компоненты внутри держатся на термоклее. Вот такая техническая кухня.
Электроника
Охххх… Ну что я могу сказать, с электроникой в этом проекте намучился сильнее всего. Первое моё родео такого масштаба.
Сердцем проекта выступил ESP8266. Дешёвый, маленький, с Wi-Fi на борту и достаточно мощный, чтобы покрыть все мои потребности. Источником головной боли стал считыватель NFC-меток - PN532. Добрая половина главы будет посвящена моим пляскам вокруг этого чудесного модуля. За аудио-визуальную красоту отвечали кольцо из 8 светодиодов и пассивный зуммер. Питает всё аккумулятор формата 18650. Бонусом к аккумулятору идут зарядный модуль TP4056 и повышающий преобразователь MT3608.
Теперь, когда мы знакомы со всеми основными участниками пьесы, перейдём к самому спектаклю. Начиналась вся работа на позитивной ноте. Я рассчитал необходимый для стабильной работы вольтаж, из медной проволоки сделал силовые линии на 5 и 3,3 вольта и землю, построил схему подключения и запасся резисторами. Через пару часов всё было припаяно и готово к тестированию.
Я подключил ESP к компьютеру, загрузил прошивку и… Тишина. Метки не считываются, светодиоды не горят. Один лишь зуммер издаёт звуки. Я попытался ещё несколько раз перезаписать прошивку и к удивлению обнаружил, что компьютер перестал видеть мою плату. При этом питание плата потребляла. После нескольких циклов втыкания-вытыкания кабеля и перезагрузки компьютера результат не изменился. Windows не определяла мою ESP. И в тот момент началась, без преувеличения, самая трагичная арка всего моего рассказа.
Тогда в голове я перебрал огромное количество вариантов возникновения проблемы. Начиная с бракованной платы, заканчивая сгоревшим USB-портом компьютера. От безысходности я даже заказал программатор, чтобы отправлять прошивку на плату в обход стандартного подключения по кабелю. Но даже с программатором мне не удалось заставить плату работать. Как выяснится чуть позже, всё дело было в…
А вы знали, что не все линии данных работают от 3,3 вольт? Я вот не знал. А ещё не знал, что конкретно мой модуль PN532 должен питаться от 3,3 вольт, а не от 5. Такой вот коктейль из перенапряжения привёл к пробою стабилизатора на ESP. В результате чего плата начала греться и перестала корректно работать.
Пришлось полностью пересматривать схемотехнику. И что я могу сказать, новая схема оказалась на порядок лучше старой.
Добавил конденсаторы, чтобы минимизировать влияние помех и наводок на работу модулей. Встроил P-канальный MOSFET, который позволил практически полностью обесточивать всё устройство во время сна. По моим подсчётам, консолька будет работать на одном заряде аккумулятора чуть больше недели. Ну и самое главное - Level Shifter. Он превращает 5-вольтовые логические сигналы в 3,3-вольтовые. И наоборот. С новой схемой устройство было собрано, запрограммировано и, наконец, запущено.
Вы ещё помните, как в рассказе о дизайне я упомянул одну маленькую кнопочку? Пришло время о ней поговорить.
Кнопочка находится в слоте для картриджа и нужна для того, чтобы консолька узнала, что пора считывать метку и запускать игру. Без кнопки пришлось бы постоянно держать PN532 включённым. А в рабочем состоянии модуль этот крайне прожорливый. Полностью разряжает аккумулятор всего за несколько часов. Так что эта маленькая кнопочка способна обеспечить целые дни автономной работы: пока картриджа в слоте нет, считыватель спит, а консоль не тратит заряд на пустые опросы модуля.
Программирование
Вот мы и добрались до устройства мозга всего устройства. И устроен он довольно просто.
В каждый момент времени устройство находится в одном из заранее известных состояний. Например, Boot, Sleeping, Ready, ReadingTag и т.д.
И в зависимости от контекста и внешних условий переключает своё внутреннее состояние. Но поговорить в этой главе я хочу не о состояниях, а об одной киллер-фиче.
Раньше я об этом нигде не упоминал, но моё устройство не только запускает игры, но и проигрывает заглавную мелодию игры и связанную с игрой LED-анимацию.
И хранил я эту интригу потому, что для меня это было именно программистское испытание.
Изначально я думал, что проблем доставят параллельное воспроизведение звука и анимации. Всё-таки на ESP8266 важно правильно работать с таймингами, чтобы не ломать параллельно работающие процессы. Однако палки мне в колёса вставила… память.
Первой мыслью, пришедшей мне в голову, было хранить последовательность нот и кадров LED-анимаций на самой плате ESP. Но я быстро отмёл эту мысль. Память платы и так довольно мала, а если на ней хранить ещё и какие-то длинные последовательности, то места хватит буквально на пару-тройку пресетов. Не более.
Затем я подумал о модуле SD-карты. Но этот вариант также был неудобен. Нужно было бы в очередной раз переделывать схему компонентов и подгонять корпус. Да и добавлять новые пресеты было бы неудобно. Каждый раз извлекать карту памяти, вставлять её в компьютер, а затем возвращать всё в консоль. Слишком геморройно.
А затем я вспомнил, что на самой метке есть целых 504 байта памяти. А значит, нужные для воспроизведения пресеты можно хранить прямо на ней. Но и тут есть загвоздка. Если мелодию можно хранить в виде последовательности “НотаПродолжительность”, например C4n3C4, то что делать с LED-анимацией? Её же не получится просто закодировать. Или всё-таки получится?
После нескольких мозговых штурмов и экспериментов я придумал небольшой, простенький и компактный язык скриптов для LED-анимации.
Как же он работает? Каждая команда - одна буква плюс числовые параметры. F - заливка, C - очистка, P - пиксель, R - сдвиг по часовой, r - против. И так далее, вплоть до E (симметричная волна из точки) и t (плавная интерполяция результата следующей команды). Всего вышла 31 команда.
И с языком скриптов анимации стали выглядеть вот так: F255,60,10;M;~10;W80;?70;S255,150,50;W50;#255,60,10,40;K60;T0,0,0,1200
Заливка и очистка
F r,g,b Залить всё кольцо цветом
C — Погасить всё
Пиксели
P idx,r,g,b Зажечь пиксель idx (с обёрткой по кругу)
G start,end,r,g,b Зажечь диапазон пикселей (с обёрткой)
N idx,percent Установить яркость пикселя idx в percent%
Сдвиг (ротация кольца)
R — Сдвинуть все пиксели на 1 по часовой стрелке
r — Сдвинуть все пиксели на 1 против часовой
O count Сдвинуть на count позиций по часовой
o count Сдвинуть на count позиций против часовой
Яркость — вся лента
D amount Уменьшить яркость каждого пикселя на amount
B amount Увеличить яркость каждого пикселя на amount
* percent Масштабировать яркость всех пикселей в percent%
Яркость — один пиксель
Q idx,amount Уменьшить яркость пикселя idx на amount
q idx,amount Увеличить яркость пикселя idx на amount
Цветовые операции
+ r,g,b Добавить цвет ко всем пикселям (аддитивный бленд)
- r,g,b Вычесть цвет из всех пикселей
# r,g,b,amount Смешать все пиксели к цвету (amount 0–255, где 255 = полная замена)
H amount Сдвинуть оттенок (hue) всех пикселей на amount (0–255)
I — Инвертировать цвета всех пикселей
Градиент и радуга
A r1,g1,b1,r2,g2,b2 Линейный градиент от первого цвета к второму по кольцу
@ offset,spread Радуга: hue от offset, распределяется по spread пикселей
Симметрия
= — Отразить первую половину кольца (0–3) во вторую (4–7)
Случайность
S r,g,b Зажечь случайный пиксель цветом
Y count,r,g,b Зажечь count случайных пикселей цветом
~ intensity Шум: добавить случайное ±intensity к каждому каналу каждого пикселя
Время
W ms Ждать ms миллисекунд
Z min,max Ждать случайное количество мс от min до max
Анимации и переходы
T r,g,b,ms Плавно перевести все пиксели к цвету за ms. Шаг фиксированный ~16 мс (ms/16 шагов по 16 мс).
U r,g,b,ms Мгновенная вспышка цветом + плавный фейд в чёрный за ms. Шаг фиксированный ~16 мс.
E idx,r,g,b,ms Симметричная волна из пикселя idx: сначала idx, затем idx±1, idx±2, … до противоположной точки. Шаг между волнами = ms (это пауза МЕЖДУ волнами, а не общая длительность). Для N=8 — 5 волн (idx; idx±1; idx±2; idx±3; idx+4 ≡ idx−4), суммарное время = 5 × ms. По завершении волны все 8 пикселей кольца залиты цветом r,g,b.
t ms Префикс-модификатор для следующей команды: плавно интерполирует её результат за ms. Шаг фиксированный ~16 мс, как у T.
Очень удобно и расширяемо.
Как работает парсер этих команд, я даже пытаться объяснить не буду. Он чуть не свёл меня с ума, и возвращаться к нему я не хочу.
Внешняя инфраструктура
Ну и как же такой проект, да без внешней инфраструктуры? Для обслуживания картриджей и консольки пришлось набросать несколько микросервисов.
Первый сервис, пожалуй, самый простой. Он берёт музыкальный трек в формате mp3/wav/mid и конвертирует его в последовательность для метки картриджа. Например, тема главного меню из Hollow Knight превращается в “C4n6G4n6Gs4n6C4n6F4n6G4n6C4n6Ds4n6F4n6C4n6D4n6Ds4n6D4n6As3n6C4n6G4n6Gs4n6As3n6G4n6F4n6”.
Конвертеру можно задавать продолжительность выходного трека, минимальные/максимальные октавы, максимально/минимально допустимую частоту и ещё пару незначительных параметров.
Второй сервис нужен в основном для работы с метками и ESP.
Такая небольшая веб-страничка, на которой можно посмотреть состояние подключённой ESP, проверить зарегистрированные действия для бэка, перезаписать данные для меток и воспроизвести на плате тестовые LED-анимации и мелодии.
Ну и последний сервис — бэкенд-сервер. Собственно, отвечает за исполнение команд, приходящих от ESP, работает с базой данных зарегистрированных меток и команд и просто тратит ресурсы моего компьютера.
Если заглянуть под капот, то вся эта инфраструктура написана на FastAPI и Angular. FastAPI отвечает за бэкенд и общение с метками и ESP, Angular - за веб-страничку, на которой удобно тыкать кнопки и наблюдать за состоянием платы. Сервисы живут в локальной сети и общаются друг с другом обычными HTTP-запросами. Не стал добавлять полноценные б.д., брокеры, очереди и прочую умную и серьёзную мишуру, которая в домашнем проекте была бы избыточна.
Результат
Вот и подошли к концу мои злоключения. Корпус напечатан, электроника собрана, код написан, картриджи собраны. Включаю устройство и вижу вот такой прекрасный результат:
Сейчас на моей полке стоит небольшая уродливо-милая консоль. Рядом — несколько картриджей. Я протягиваю руку, беру картридж с фигуркой игры, вставляю его в консоль и слышу приятный щелчок. Подсветка играет анимацию, зуммер воспроизводит мелодию из главного меню игры, ну а я вновь испытываю эйфорию от запуска игры.
На проект ушло около двух месяцев и несколько тысяч рублей. Доволен ли я результатом? Несомненно! Буду ли я пользоваться этим устройством? Вряд ли. Получился забавный и интересный проект, в котором я многому научился. Впервые использовал конденсаторы, сталкивался с перегревом, много паял, разводил схему проводки и закапывался в даташиты. Использовать же устройство я не буду по одной простой причине. Картриджи. Они очень красиво и эстетично выглядят, но их изготовление занимает слишком много времени. Напечатать корпус, вырезать оргстекло, собрать фигурку, конвертировать мелодию, придумать анимацию, записать данные на метку. Всё это в совокупности занимает несколько часов, и так заморачиваться ради каждой понравившейся игры я вряд ли буду.
Но знаете, даже если консоль я в будущем ни разу не включу, она уже заняла особое место на полке и в сердце. Этим проектом я хотел выразить мою любовь к играм, к процессу создания и к творчеству в целом. И оно того стоило. Не жалею ни о секунде потраченной на работу над этим проектом. И желаю каждому прочитавшему творить и реализовывать свои творческие задумки!
Лучшие комментарии
Морги два раза, если это она, заставила тебя это написать. 🤔
Говорил про прикол тактильности, а в результате тактильность и не показал. На видео картридж сразу вставлен =(
Ну это плюс вайб, плюс талант и равно блог недели 😎
Я живу жизнь в соло. Так шо и наслаждаюсь ароматами пластика, канифоли и сырного борта)
А за приятные слова спасибо!
Смочь такое сотворить не каждый сможет. Молодец, огромная работа проделана 🔥
Ну вот, видно что рукастый и с мозгами человек, приятно видеть результат работы, сделано хорошо, да даже если консоль и будет стоять на полочке и не использоваться, всё равно будут приятные воспоминания о ней. Молодец.
Ты прав)
Хотел добавить гифку в блог, но она не показывается в поиске. Абсолютно не понимаю, как работают теги и поиск в Klipy
Эххххх, звук потерялся на Klipy(
Хотя, может оно и к лучшему. А не то все комментарии бы выглядели как-то так
Тоже натыкался на подобные видео и раздумывал о том, чтобы сделать свою стим коллекцию физической. А потом прикинул сколько лишних телодвижений к запуску игры это добавит и как сильно я не буду этим пользоваться, потому что вечно гоняюсь за оптимизацией, ускорением процессов и удобными интерфейсами и дальше идеи это не ушло.
Приятно видеть кого-то, кто не просто вдохновился тем же концептом, но и улучшил его :)
Видео со звуком давай! =)
С косарём
Побольше бы такого добра нам в блоги без шуток.
Казалось бы, при чём тут игры?
Умаслил так умаслил)
Я хоть никогда не понимал этой «тактильности». Но чёрт тебя бери! Когда что-то подобное делают, а ты сделал консоль! Мне только остаётся восхищаться, какой я рукожоп
+ rep
Блин очень круто, когда-то хотел сделать нечто подобное но скорее как автор видео которого ты упомянул, кстати можно сыллку?
fireflowerчего только не сделают люди, лишь бы не играть в игры
При всём моём уважении к фетишистам и материализму, я бы лучше нюхал и трогал любимую девушку, а не коробочки с пластиком и железом...
Но за порыв и инициативу браво!