Новая жизнь советского джойстика
Так получилось, что уже долгое время в моей рабочей зоне лежал отечественный джойстик типа «джойстик», а точнее даже целый «игровой манипулятор», Веста ИМ-01. Маленький и симпатичный, подозрительно напоминающий джойстик ProLine для Atari 7800, он достался мне в комплекте с одним из приобретённых в начале года Спектрумов, и с тех пор периодически мозолил глаза, вызывая вопрос — что бы с ним сделать? У меня уже есть такой же в коллекционном состоянии, этот же немного повидал виды, и его не особо жалко перепрофилировать под какие-нибудь интересные нужды. Можно было бы приспособить его для клонов Atari 2600, которых у меня чуть больше, чем джойстиков к ним, но это же скучно.
Также хотелось бы для разнообразия сделать что-то простое, что не затянется на месяцы и годы, получив удовлетворение от небольшого быстро законченного проекта. И тут я вспомнил один незакрытый гештальт из середины 2000-х годов. Так родился этот маленький проект выходного дня, с очень низким порогом вхождения, который может повторить почти любой желающий.
Суть проекта: простейшая самодельная игровая приставка для телевизора, встроенная в корпус классического джойстика.
▍ Программное видео
В конце 1990-х 8-битные микроконтроллеры от Atmel и Microchip начали обладать довольно приличной производительностью (3–8 MIPS), и каким-то образом в головах энтузиастов зародилась идея попытаться программно генерировать на них чёрно-белый видеосигнал. Одним из первых это проделал Rickard Gunée, создавший в 1998 году вариант игры Pong на контроллере PIC16F84. Публикация проекта в сети Интернет послужила толчком, и в начале 2000-х расцвела целая россыпь проектов самодельных телевизионных игровых устройств.
ATmega32
Масла в огонь подлило скорое появление значительно более производительных МК, AVR ATmega (16–20 MIPS) от Atmel и PIC-совместимых контроллеров SX28 и SX52 от Parallax (80 MIPS). Чуть позже, в 2006 году, подоспел и новый 32-битный и восьмиядерный МК Parallax Propeller, обладавший невероятной по тем временам мощностью, по 20 MIPS на ядро, и наиболее ярко проявивший себя именно в задаче программной генерации видеосигнала.
Своего пика идея достигла во второй половине 2000-х. Это, в частности, проект XGameStation известного в прошлом автора книг на тему разработки игр, Андре Ла Мота — начавшийся в 2004 году, он прошёл через ряд итераций, в которых было задействовано практически всё вышеперечисленное железо. Окончательно закрыла вопрос нашумевшая демка Craft, взявшая первое место на Breakpoint 2008, и популярный проект UzeBox/Fuzebox 2009 года на базе ATmega64 и микросхемы NTSC-энкодера.
В то время я с интересом следил всеми за этими движениями. Возможность создания самодельного компьютера всего на нескольких микросхемах вместо сотни, как раньше, очень интриговала, и на протяжении нескольких лет я придумывал различные проекты на основе AVR, в силу разных причин так и оставшиеся в виде схем и расчётов. Далее стали доступны недорогие TFT-экранчики, и самодельные игровые платформы массово взяли новый курс в портативность, что мне уже было не особо интересно, да и в целом тема исчерпала себя.
Однако, всё старое — недостаточно забытое новое, а история имеет свойство развиваться по спирали. В 2018 году идея программной генерации видеосигнала на МК возродилась на качественно новом уровне благодаря семейству ESP и особенности реализации интерфейса i2s на них, который способен разгоняться аж до 80 мегагерц. Это открыло возможность программной реализации сразу цветного композитного сигнала стандартов NTSC и PAL на ESP32 — уже без применения дополнительных микросхем-кодеров.
ESP32 как бы излучает свою скрытую мощь
Теперь это даже делается немного попроще. Уже не нужно писать хитро сложенный, точно выверенный по таймингам код на ассемблере, считая соответствие тактов МК и пикселей растра. Код пишется на C, и хотя для реализации цветного композитного видео он весьма запутан, есть готовая библиотека, доступная прямо из Arduino IDE.
Вспомнив дела дней минувших, я понял — пока композитное видео не окончательно ушло в прошлое, самое время реализовать старые хотелки, и воспользовавшись открывшимися возможностями и накопленным опытом, соорудить на коленке собственное простейшее самодельное игровое устройство, разместив его электронику в корпусе так кстати подвернувшегося под руку ни в чём не повинного советского джойстика.
Проект состоит из трёх частей: механической, электронной и программной.
▍ Механическая часть
Оригинальные джойстики-палки от Atari, и в особенности Proline, не отличались точностью управления, и в общем их качество было очень так себе. Что уж говорить о советском клоне. Так как джойстик просто кошмарный, я решил попробовать заменить его контактную группу из пружинящего металла на микровыключатели, приблизив точность и ощущения к аркадному стику. Также хотелось добавить пару служебных кнопок, по возможности не очень заметных, чтобы не портить внешний вид — в финальной прошивке они были задействованы для запуска и переключения игр.
Надо сказать, исходя моего из прошлого опыта, что проектирование и изготовление любых механических кнопок, в том числе на основе готовых — обманчиво простое дело. Как правило, из-за мелких неточностей в позиционировании деталей и люфтов в системе кнопки нажимаются нечётко, или, например, стик срабатывает на один угол, но подклинивает в другом. Так что механическая часть обещала быть интересной задачкой — то, что надо для очумелых ручек.
Перед началом работы по внешнему виду казалось, что в джойстике предостаточно места, и я с наскока сделаю всё задуманное. После разборки мой энтузиазм поубавился. Хотя места внутри и немало, внутренние элементы расположены таким образом, что эффективно использовать его не выйдет.
Внутри джойстика. Контактная группа
В частности, один из двух винтов, скрепляющих половинки корпуса, не дал возможности разместить более распространённую DevKit1 версию ESP32, которых у меня в избытке. Пришлось применить версию поменьше, Wemos D1 Mini. Для размещения приличных концевиков по классической аркадной схеме места тоже не нашлось, да и сама кинематика стика с точкой опоры внизу, а не с подвешиванием посередине, не очень располагает к реализации таковой.
Примерка ESP32 к корпусу
Я решил делать схему с нижней опорой, как и в оригинале, но убрать крестовину стика и сделать её свободно вращающейся. Чтобы понять, как именно лучше переделать стик, я собрал по углам разнообразные ненужные микропереключатели разных типоразмеров, а также приобрёл ещё совсем мелких концевиков, и начал примерять их по месту. Идея применить тактовые кнопки была отброшена сразу, у них слишком жёсткое нажатие. Также были забракованы резиновые микрокнопки: они обладают хорошим ходом, но не щёлкают, а хотелось бы тактильную обратную связь. В итоге я склонился к использованию стандартных микриков из пары неисправных мышек — таких как раз набралось шесть штук.
Здесь на сцене появляется традиционный для современного DIY 3D-принтер в кустах. Да, всё, что я делал в этом проекте, без особых проблем можно было бы сделать и без него, но раз он есть под рукой — это прекрасный повод поупражняться в CAD. Да и надо же хоть как-то оправдывать приобретение 3D-принтера.
После трёх итераций с моделированием и печатью промежуточных вариантов (по полчаса каждый) я придумал вариант из трёх деталей:
Держатель микриков
Держатель для микриков, формирующий из них традиционную для аркадных стиков конфигурацию, которую приходится с опаской показывать на публике в современном мире, как бы ни увидели в ней чего.
Средняя часть
Средняя часть с гибкими лепестками, которая нужна, чтобы удлинить концевики микриков — они просто микроскопические — и сделать возможными чёткие диагональные нажатия.
Круг с дыркой
Часть, заменяющая штатную крестовину. Это просто круг с дыркой.
Финальные детали и некоторые промежуточные версии
Хотя я стараюсь всегда следовать заветам Бена Хека и делать все самоделки максимально разборными, в данном случае я решил не тратить винтики M2.5 и просто вклеить микрики в держатель. Если что, всегда можно их отломать и приклеить другие, или напечатать держатель заново. Если что случилось сразу же, один вклеился чуть-чуть кривовато. Хотя это и было всего лишь примерочное вклеивание на тестовую капельку суперклея, отламывание заняло несколько минут, так что способ крепления оказался достаточно надёжным.
Держатель микриков просто вкладывается в корпус. У него предусмотрен небольшой люфт, выбираемый прокладками из изоленты для более точного позиционирования. Для того, чтобы держатель поместился, пришлось удалить пару выступающих частей внутри корпуса, на которые крепилась оригинальная контактная группа. Средняя часть привинчивается двумя винтиками на место контактной пластины, она вполне неплохо фиксирует блок микриков. Круг с дыркой приклеен на крестовину, концы которой были срезаны, чтобы обеспечить возможность свободного вращения стика.
К сожалению, штатную пружину, дающую небольшой натяг ручке джойстика, пришлось убрать. Она слишком жёсткая, и просто не давала нормально закрыть корпус. Какая-то хотя бы формальная мягкая пружинка не помешала бы, но материалов для неё не нашлось, да и без неё работает терпимо.
Крестовина в сборе
Следующей задачей были основные кнопки действия. Неожиданно они оказались более сложной задачей, чем стик.
Сначала я напечатал затычки для внутренней части штатных кнопок, чтобы придать им равномерную плоскую поверхность. Тут опять возникла проблема слишком маленькой высоты штока микриков от мышки: при небольшом перекосе кнопки, при нажатии на один из её углов, срабатывания микрика не получалось, а разместить удлиняющую проставку было уже просто негде.
Площадка-затычка для кнопок действия
Я перебрал запасы микриков ещё раз и выбрал пару от значительно более старой мышки, у которых высота штока оказалась чуть-чуть больше. Напечатал для них постамент и подрезал корпус модельным ножом, чтобы расположить штоки микриков максимально близко к центрам кнопок. Основная хитрость была в подборе наилучшего расположения постамента, чтобы обе кнопки срабатывали одинаково чётко — доля миллиметра в сторону, и чёткость нажатий ухудшалось.
Сначала я выбрал положение постамента путём примерок с прижатием пальцами, потом слегка закрепил на две маленькие капельки суперклея, не сильно прижимая пластик, и когда правильное положение было поймано, окончательно зафиксировал клей сильным прижатием. Далее обычная тема с укреплением конструкции суперклеем и содой для надёжной фиксации.
Кнопки действия
Теперь служебные кнопки. Это просто тактовые кнопки, под которые просверлены дырки на противоположной стороне кнопкам действия. Так как штоки кнопок чёрные и лишь чуть длиннее, чем толщина пластика корпуса, они не особо бросаются в глаза. Сами кнопки приклеены всё тем же клеем с содой на напечатанные постаменты, расположенные таким образом, чтобы они располагались напротив отверстий, не мешали закрывать корпус и не проваливались внутрь корпуса от нажатий. Кнопки работают чётко, но нажимаются не так легко, как кнопки действия — то, что нужно для редко используемых кнопок.
Служебные кнопки
Последняя задача — крепление платы ESP32 в корпусе. Чтобы иметь возможность загружать разные прошивки, не разбирая корпус, я решил сделать отверстие под MicroUSB. А на случай выхода платы из строя — всякое бывает — не приклеивать её намертво. Для начала выбрал место и вырезал дырку под разъём модельным ножом. Далее была создана очередная 3D-модель — две П-образные скобки, напечатана и вклеены в корпус. Плата защёлкивается в них внатяг. На противоположной стенке вклеил упор из пластика, а на внутреннюю сторону верхней крышки — пару войлочных ножек для мебели, чтобы плата точно не имела возможности выскочить из креплений или сдвинуться при вставлении USB-разъёма.
Закреплённая на своём месте в корпусе плата
Честно скажу, что итоговый результат тянет скорее на троечку, хотя в процессе работы казалось, что получается на твёрдую четвёрку. В целом джойстик вышел функциональный, стало значительно лучше, чем было. К кнопкам действия претензий нет вообще. Однако главная часть, а именно стик, работает не очень хорошо. Сам по себе он вполне чёткий, приятно щёлкает, давая обратную связь. Но его ход довольно маленький, а сам корпус джойстика оказался недостаточно жёстким, и всего с двумя точками соединения половинок невозможно отрегулировать всё так, чтобы и нажатия происходили чётко, и нельзя было случайно прожать микровыключатели стика через сжатие корпуса. В общем, играть можно, но осторожно.
Всё это безобразие в сборе
С корпусными работами всё, наконец-то переходим к пайке.
▍ Электроника
Схема устройства максимально проста, она состоит из собственно микроконтроллера, резистора, конденсатора, кнопок и вязанки проводов.
Схема устройства в максимально наглядном виде
Резистор и конденсатор образуют простейший ФНЧ для звука, который генерируется через PWM, и потому требует отсечения высокочастотной несущей. Видеовыход подключается к видеовходу телевизора напрямую, уровень сигнала нормальный без всяких усилителей-повторителей. Кнопки для простоты подключаются без резисторов подтяжки, так как они есть внутри МК.
Далее нужно было сделать провода для подключения. Для аутентичности я решил попробовать использовать оригинальный провод, хотя полагал, что качество передачи видеосигнала будет оставлять желать лучшего (по факту оно оказалось нормальным). Также я подобрал пару ненужных AV и USB проводов.
В итоге получилась такая конструкция: оригинальный полутораметровый провод на одном конце, на другом он разделяется на два метровых провода, со стандартными разъёмами RCA для подключения к ТВ и разъёмом USB для питания через телефонную зарядку или пауэрбанк. Соединение проводов спрятано в чёрную термоусадку. Внутри корпуса провод подсоединяется к электронике через разъём, для удобства и на всякий случай.
▍ Наладка
На этапе пайки и проверки соединений пришлось выяснить распиновку микриков с помощью прозвонки — как оказалось, несмотря на их внешнее сходство, они бывают с разными распиновками. Так обнаружилось, что один из микриков действия очень сильно дребезжит, не давая надёжного нажатия — поленился проверить до приклеивания. Всё-таки та мышка была очень старой. Пришлось отклеить с помощью скальпеля и пассатижей, и приклеить другой. Хоть он и был с более коротким штоком, всё-таки удалось разместить его так, чтобы он надёжно срабатывал при нажатии на любой угол кнопки.
После завершения сборки я сделал простейший тест для кнопок с выводом в консоль. На этом этапе возникла традиционная путаница с нумерацией пинов, подтяжками, и дублирующими функциями на пинах.
Кнопки я изначально подключал чисто по удобству протяжки проводов и статусу доступности пинов на картинке с распиновкой. Но в этом деле часто встречаются подводные камни, так как на картинках вряд ли можно уместить информацию об особенностях каждого из пинов. И, разумеется, подводный камень обнаружился: кнопки A, B, Y я подключил к GPIO39, 35, 34, а пины с номером от 34 и выше на ESP32 не имеют встроенной подтяжки, и так как для упрощения схемы я не использовал внешние резисторы подтяжки, они оказались висящими в воздухе. Пришлось выбрать другие пины.
▍ Программная часть
Железо готово, чешущиеся руки почёсаны. Но пока оно не делает ничего интересного. Начиная проект, и даже уже сделав почти всю работу по механической части, я не вполне определился, что именно я хочу прошить в МК. Какой же функционал засунуть внутрь?
Первое, что приходит в голову — можно взять ESP_8_BIT, уже готовый эмулятор Atari, NES, SMS. Впрочем, он рассчитан на беспроводные джойстики и клавиатуры, у меня же механические кнопки с прямым подключением. Также он не отличается стабильностью, часто глючит и падает. Нормальных эмуляторов и консолей у меня более чем хватает, интереса в ещё одном эмуляторе, только плохо работающем, я не увидел. Тем не менее, я адаптировал его в качестве proof of concept — доработал код для работы с реальными кнопками, убедился в наличии картинки и звука.
Меню эмулятора Atari на ESP_8_BIT
Модификация кода очень незначительная и очевидная, даже нет смысла выкладывать в виде файла. Добавляется определение пинов, пара функций для чтения состояния кнопок, а также трансляция их во внутренние коды клавиш эмулятора в начале функции emu_loop:
#define PIN_J_UP 32
#define PIN_J_DOWN 12
#define PIN_J_LEFT 4
#define PIN_J_RIGHT 0
#define PIN_J_A 26
#define PIN_J_B 19
#define PIN_J_X 23
#define PIN_J_Y 5
#define JOY_UP 0x0001
#define JOY_DOWN 0x0002
#define JOY_LEFT 0x0004
#define JOY_RIGHT 0x0008
#define JOY_A 0x0010
#define JOY_B 0x0020
#define JOY_X 0x0040
#define JOY_Y 0x0080
void joy_init(void)
{
pinMode(PIN_J_UP, INPUT_PULLUP);
pinMode(PIN_J_DOWN, INPUT_PULLUP);
pinMode(PIN_J_LEFT, INPUT_PULLUP);
pinMode(PIN_J_RIGHT, INPUT_PULLUP);
pinMode(PIN_J_A, INPUT_PULLUP);
pinMode(PIN_J_B, INPUT_PULLUP);
pinMode(PIN_J_X, INPUT_PULLUP);
pinMode(PIN_J_Y, INPUT_PULLUP);
}
unsigned int joy_read()
{
unsigned int joy = 0;
if (digitalRead(PIN_J_UP) == LOW) joy |= JOY_UP;
if (digitalRead(PIN_J_DOWN) == LOW) joy |= JOY_DOWN;
if (digitalRead(PIN_J_LEFT) == LOW) joy |= JOY_LEFT;
if (digitalRead(PIN_J_RIGHT) == LOW) joy |= JOY_RIGHT;
if (digitalRead(PIN_J_A) == LOW) joy |= JOY_A;
if (digitalRead(PIN_J_B) == LOW) joy |= JOY_B;
if (digitalRead(PIN_J_X) == LOW) joy |= JOY_X;
if (digitalRead(PIN_J_Y) == LOW) joy |= JOY_Y;
return joy;
}
void emu_loop()
{
unsigned int joy = joy_read();
//emu controls
_emu->key(82, (joy & JOY_UP) ? 1 : 0, 0);
_emu->key(81, (joy & JOY_DOWN) ? 1 : 0, 0);
_emu->key(80, (joy & JOY_LEFT) ? 1 : 0, 0);
_emu->key(79, (joy & JOY_RIGHT) ? 1 : 0, 0);
_emu->key(40, (joy & JOY_X) ? 1 : 0, 0); //start
_emu->key(43, (joy & JOY_Y) ? 1 : 0, 0); //select
_emu->key(225, (joy & JOY_A) ? 1 : 0, 0);
_emu->key(226, (joy & JOY_B) ? 1 : 0, 0);
//gui controls, needs one shot events
static unsigned char gui_call = 0;
if ((joy & JOY_X) && (joy & JOY_Y))
{
if (gui_call < 10) ++gui_call;
}
else
{
gui_call = 0;
}
gui_key(58, gui_call == 9 ? 1 : 0, 0); //gui call
static unsigned int joy_p = 0;
unsigned int joy_t = (joy ^ joy_p)&joy;
joy_p = joy;
gui_key(82, (joy_t & JOY_UP) ? 1 : 0, 0);
gui_key(81, (joy_t & JOY_DOWN) ? 1 : 0, 0);
gui_key(80, (joy_t & JOY_LEFT) ? 1 : 0, 0);
gui_key(79, (joy_t & JOY_RIGHT) ? 1 : 0, 0);
gui_key(40, (joy_t & JOY_A) ? 1 : 0, 0); //ok
gui_key(58, (joy_t & JOY_B) ? 1 : 0, 0); //cancel
//... далее как в оригинальной версии
На этом, казалось бы, и всё. Но хотелось бы задачку чуть поинтереснее, чем простая сборка готового софта, и вообще что-то менее банальное.
Я обдумал различные варианты. Писать игры непосредственно под железо или под свою виртуальную машину, как это делает каждый следующий автор — без толку, задача расширяется на создание софта или поиск энтузиастов, которые его напишут. Это совсем не то, чем хотелось бы заниматься, и выходит далеко за рамки проекта выходного дня.
Следующей идеей было портирование какой-то готовой виртуальной машины. Например, классического CHIP8 или Little Game Engine от ESPboy. Конечно, это открывает возможность простого написания своих игр, и также решает давнюю концептуальную проблему всех самодельных приставок и компьютеров на МК — необходимость перепрошивки МК для смены софта. Но по сути это не сильно интереснее адаптации ESP_8_BIT — то же самое, только со странным разрешением экрана и малым количеством не особо привлекательных игр. Заниматься этим мне не очень захотелось.
И тут в голову наконец-то пришла идея, которая для меня должна была быть крайне очевидной, но почему-то подзадержалась в пути. Я понял, какую именно игру хочу видеть внутри советского джойстика: советскую видеоигру, которая занимает особое место в моей памяти.
▍ S.A. M.E.
Где-то в самом конце 1980-х или в начале 1990-х во многих кинотеатрах и торговых центрах появились советские игровые автоматы «Фотон». Впрочем, название автомата его юным пользователям вряд ли было известно, но была хорошо известна главная игра на нём: «Питон». Это продвинутая вариация классической «Змейки», но цель другая — съесть всех зайцев, прежде чем они съедят капусту на уровне. Знакомство с этой игрой в те годы заложило фундамент моего последующего непреходящего увлечения видеоиграми длиной в десятилетия.
Игровой автомат «Фотон». Свободное фото из Википедии
Именно эту игру я и решил поместить внутрь джойстика.
Ещё один рояль в кустах, к которому приводит упомянутое выше увлечение, заключается в том, что много лет назад я немного приложил руку к развитию программной эмуляции советских игровых автоматов и компьютеров. В частности, в 2006 году я сделал один из первых proof of concept эмуляции автомата ТИА-МЦ-1, а позже и, вероятно, первый, но так в своё время и не опубликованный эмулятор компьютера Сура/Веста/Хобби ПК8000, на базе которого сделан «Фотон» — вместо релиза я снял и передал дамп ПЗУ автору более продвинутого мультисистемного эмулятора b2m, резонно посчитав, что так будет эффективнее.
В 2016 году я также сделал для Музея советских игровых автоматов эмулятор SAME (Soviet Arcade Machine Emulator), изначально существовавший в виде браузерной Flash версии на AS3, и позже в виде порта под Android на Haxe, включавший в себя ТИА-МЦ двух разных версий, «Фотон», и «Истребители».
SAME в версии для Android. Игра «Снежная королева» для ТИА-МЦ1
Разумной идеей было не ограничиваться только Питоном, а портировать SAME целиком, разве что исключив Истребители, так как эта игра для двоих игроков.
Сначала я портировал код с AS3 на C в тестовом окружении на PC на основе SDL, это было несложно. Иметь под рукой работающий на PC эталонный эмулятор очень удобно в целях отладки, так как на самом ESP отладочные средства не очень-то доступны.
Потом нужно было перенести эмулятор на ESP32 и подключить программную генерацию видеосигнала. В менеджере Arduino IDE уже есть соответствующая библиотека, извлечённая из кода вышеупомянутого ESP_8_BIT.
По причине своего происхождения библиотека ограничена палитрой R3G3B2, но это не проблема, так как у Фотона грубая RGB-палитра типа Спектрумовской, а у ТИА-МЦ-1 те же 256 цветов с тем же кол-во бит в каналах, и уровни сигналов не сильно отличаются, выглядит нормально. ТИА-ЦМ использует странное разрешение 256 на 256, с реально видимыми 256 строками, которые реально используются во всех играх. На нормальном телевизоре такое видно быть не должно, стандартный растр включает максимум 240 строк. Пришлось немного обрезать картинку сверху и снизу, а для Фотона с его 192 строками наоборот, добавить бордюр.
Отладочное веселье началось, когда я приступил к переносу кода. Он без проблем собрался, но программа падала на старте. Как оказалось, МК не может выделить требуемое количество ОЗУ, хотя на борту его более чем достаточно. Дело в устройстве оперативной памяти ESP32: это не линейные 520 килобайт ОЗУ, а набор разрозненных разнотипных блоков, часть из которых может работать только в 32-битном режиме, а часть (уже по программным причинам) не может быть выделена в виде статического массива.
Ранее мне никогда не нужно было выделять настолько много памяти, и с этой проблемой я столкнулся впервые. Пришлось призадуматься над решением.
Компиляция ROM’ов в виде const массивов для размещения их во Flash-памяти в этом проекте не очень подходила, так как пришлось бы делать достаточно сложные функции выборки, собирающие байты из разных массивов, что сказалось бы на скорости работы.
Для начала я помудрил с выделением памяти. Вместо создания и удаления массивов по необходимости, я выделил общую память в виде двух фрагментов по 64 килобайта на запуске прошивки. Далее она используется для всех трёх эмуляторов путём переназначения указателей. Также избавился от лишних массивов, немного переделал рендер.
Фотон заработал. Для ТИА-МЦ половина игр не заработала, а заработавший S.O. S. очень заметно тормозил.
Проблема с запуском была решена быстро: при переносе кода потерялся один из вызовов инициализации эмулятора.
Проблемы с торможением мне было не вполне понятны, ведь на более сложном ESP_8_BIT игры эмулируются без тормозов. Тратить слишком много времени на решение этой проблемы не хотелось, и я попытался обойти её довольно грубым способом — снижением частоты перерисовки экрана до 25 кадров в секунду. В принципе это сработало, стало играбельно. Однако, из-за особенностей устройства эмулятора это повлекло за собой проблемы со звуком, выражающиеся в снижении его высоты вдвое от нужной, хотя качество звучания было довольно приличным.
Я оставил проект в таком виде на некоторое время, и вернулся к вопросу уже при написании статьи. Посмотрев код, стало ясно, что значительное замедление вносит именно код рендера звукового потока. Он без проблем работал на PC, но куда более ограниченной производительности ESP32 ему не хватало. Я полностью переписал этот код, что дало приличное ускорение и устранение проблемы высоты звука, но ценой заметного снижения качества звука, так как пришлось отказаться от какой-либо интерполяции. Выбор получился так себе, типа бороды над или под одеялом, и пока я оставил текущий вариант.
Конечно же, нельзя обойтись без картинки с Коньком-Горбунком
▍ Цена вопроса
В заключение — примерная оценка затрат на проект на случай если вы захотите повторить его своими силами.
Для начала нужен джойстик. Если вы тоже хотите взять простой советский, на барахолках они стоят до тысячи рублей. Пара дохлых мышек условно бесплатно, аналогичные новые микровыключатели или концевики стоят 30–50 рублей за штуку.
Если же интересна только программная часть, можно пойти другим путём. Разнообразные современные и не очень геймпады стоят примерно столько же, но переделывать их механику не нужно. Правда, если вы захотите использовать и электронику без переделок, придётся дорабатывать прошивку, но всегда можно просто подключить напрямую контактные площадки.
Платка с ESP32 в местных магазинах стоит около 500 рублей, в Китае на сотню дешевле.
Провода в хозяйстве обычно уже есть лишние, средняя цена провода на барахолках около 50 рублей.
Также вам понадобится инструмент. Предполагается, что он у вас уже есть. Обойтись можно очень минимальным набором, но чем больше инструмента хорошего и разного, тем лучше:
- Модельный скальпель.
- Отвёртки.
- Кусачки-бокорезы.
- Шуруповёрт со свёрлами.
- Много суперклея и соды.
- Паяльник.
- 3D-принтер по вкусу.
Если вы используете 3D-моделирование, вам понадобится снимать размеры с имеющихся деталей. В этом очень помогает пластиковый штангенциркуль — он не очень точный, но не царапает детали.
Исходный код проекта можно скачать здесь.
Выиграй телескоп и другие призы в космическом квизе от RUVDS. Поехали?