На связи Артём Ковальчук. Сетевик, немного блогер и по совместительству ведущий подкаста.
Последний год мы вчетвером — Марат Сибгатулин, Ирина Маркова, Иннокентий Солнцев и я — копаемся в архивах и записываем подкаст про историю связи. Но в сентябре прошлого года мне захотелось дать людям потрогать историю руками.
Идея была на стыке ретрофутуризма и истории: создать видеоигру, в которой контроллером будет не мышь и даже не клавиатура, а самый настоящий телеграфный ключ.
Игроку предлагалось вооружиться телеграфным ключом и «настучать» морзянкой столько слов, сколько получится за отведенные 90 секунд. Кто набрал больше слов, того и приз.
В этой статье разберем детали реализации и посмотрим, что получилось в итоге.

Кодим
Идея родилась в сентябре 2025-го, вызревала до январских праздников, и уже 10 января был готов рабочий прототип, написанный с помощью Windsurf. Каркас был написан за пару дней в новогодние праздники!
На старте я накидал модульную структуру проекта. Дополнительный плюс - в таком варианте проще взаимодействовать с нейронкой.
morse_dev/ ├── main.py # Точка входа в приложение ├── src/ │ ├── app.py # Основной класс приложения и главный цикл │ ├── core/ # Ядро бизнес-логики │ │ ├── config.py # Конфигурация и настройки │ │ ├── game_controller.py # Контроллер игровой логики │ │ ├── game_state.py # Управление состояниями игры │ │ ├── morse_decoder.py # Декодер азбуки Морзе │ │ └── word_generator.py # Генератор слов │ ├── hardware/ # Работа с оборудованием │ │ └── megohmmeter_control.py # Управление мегомметром │ ├── input/ # Обработка ввода │ │ ├── gpio_handler.py # Обработка GPIO │ │ └── event_handler.py # Обработка событий │ ├── ui/ # Пользовательский интерфейс │ │ └── renderer.py # Отрисовка интерфейса │ └── data/ # Управление данными │ └── high_scores.py # Таблица рекордов
Вайбкодинг хоть и проклятый, но благодаря ему я, не обладающий знаниями в электронике и средненько знающий Python, осилил этот проект и довел его до реализации.
Основной объем работы ушел на продумывание и реализацию игровой логики. Это оказался самый сложный модуль, так как ему предстояло координировать несколько систем: генерацию слов, декодирование Морзе, подсчёт очков и управление перифирией.
За 90 секунд игроку предлагается посимвольно «набить» морзянкой рандомные слова с помощью телеграфного ключа. Одно полное слово засчитывается как одно очко. Победит тот, кто наберёт больше всего очков.
На выбор два уровня сложности — легкий и сложный. Различия — в более жестких таймингах на ввод слов и размер самого слова. На легком можно встретить слово максимум из 4 символов, а на сложном уже запросто могло попасться из 6-ти.
Перед очередной попыткой можно ввести логин, чтобы потом найти себя в лидерборде.
Интерес к первоначальной простенькой реализации добавила не менее простая механика бонусного времени, которое можно было выбить как на легком, так и на сложном уровне. Всего-то нужно было набирать без ошибок 3 слова подряд, чтобы получить заветные +10 сек. на легком и +15 сек. на сложном к общему времени. Но на деле это вылилось в настоящее соревнования за первое место на сложном уровне!
Сердцем системы стал декодер Морзе. Я реализовал классический подход с хеш‑таблицей соответствий между последовательностями точек‑тире и буквами латинского алфавита. Алгоритм работы декодера основан на накоплении символов в буфере и автоматическом распознавании по таймауту.
Когда пользователь нажимает на телеграфный ключ, система измеряет длительность нажатия с точностью до миллисекунд. Здесь возникла интересная инженерная задача: как определить границу между точкой и тире при разной скорости работы пользователей?
Готовые библиотеки декодера морзянки уже есть, но для моего проекта было проще написать свой. Я решил эту проблему через адаптивный порог. Вместо фиксированного значения система анализирует среднюю длительность предыдущих символов и динамически подстраивает пороговое значение.
Тайминги соответствуют международному стандарту кода Морзе:
точка — 1 единица
тире — 3 единицы
Код распознает замыкание ключа как точку при <320ms и как тире, если держать ключ замкнутым длиннее >480 ms. После каждого символа запускается таймер LETTER_GAP. Это своего рода компромисс, чтобы дать возможность пользователю успеть набрать все символы одной буквы. Если за это время не поступает новый символ, накопленная последовательность передаётся на декодирование. Декодер ищет последовательность в хеш‑таблице и возвращает соответствующий символ. Если последовательность не найдена в таблице, система сбрасывает буфер и продолжает ожидать ввод.
Важный технический момент: LETTER_GAP в 2 раза длиннее тире и составляет 960ms, что позволяет вводить символы на достаточной для игры скорости, без преждевременного распознавания. Система не ограничивает скорость ввода — игроку лишь необходимо поймать ритм точки/тире, а ограничение касается только паузы между буквами.
Я остановился на таком примитивном подходе, так как возможность практиковаться с телеграфным ключом есть далеко не у всех. Моей главной задачей было организовать забавную активность для гостей, добавив в неё дух соперничества.
А вот описание механик, позволивших устроить из этого соревнование:
Если ошибся при вводе, то придется разобраться с ошибочным символом, иначе слово закончить не получится
Есть возможность получить бонусное время за три безошибочно введенных подряд слова (+10 сек. на легком и +15 сек. на сложном), что сильно повышает шансы вырваться вперед
Отдельно есть время на ввод слова. Если не успел — это слово пропускается и не засчитывается
Учёт ошибок при наборе, оно же «Аккуратность» в итоговой таблице. Благодаря этому у нас под конец дня произошла настоящая заруба за первое место на харде! Второе место отставало от первого всего на ОДНУ ошибку! И это при равном количестве очков — 11!
Конечно же, сам ключ! Одной рукой придерживаем, второй набиваем, так как он не был никак закреплен. Его следовало прочувствовать, иначе был шанс продолбать точку/тире из‑за неуверенного нажима и потерять драгоценные секунды.
Попыток было бесконечное количество, хотя мы изначально планировали всего 3. Хорошо, что мы этого не сделали.
Интерфейс игры сделан со стилизацией под старые BBS, вышло достаточно аутентично.

Небольшая демонстрация работы игры:
Если есть проблемы с ютубом, дублировал демо на ядиск.
А всем жилающим можно опробовать игру из репы на гитхабе.
Запустить все достаточно просто:
cd morse_gameСоздать виртуальное окружение с питоном версии 3.7 и выше, напр.
python3 -m venv ~/venv/morse_devПровалиться в виртуальное окружение, напр.
source ~/venv/morse_dev/bin/activateУстановить зависимости
pip install -r requirements.txtЗапустить командой python
main.pyв фулскрине или pythonmain.py--windowв окнеИспользовать клавишу "Пробел" для набора морзянки
Пока я писал игру, телеграфного ключа у меня еще не было, поэтому для отладки я использовал клавиатуру. Достаточно было одной клавиши "Пробел", длину нажатия которого также воспринимал декодер Морзе.
Самым интересным открытием отладки оказалась возможность отправки скриншотов игры в нейронку и ее способность понять, что я хочу исправить!
Простой пример: отправляем нейронке скриншот и просим добавить примерно в границах зелёного квадрата поле для уведомлений. Например, чтобы там выводилось бонусное время или предупреждение о том, что таймер скоро закончится и пора бы поторопиться

До такого способа — попросить её запустить игру, сделать скриншот, найти ошибку и исправить её — я тогда не замахивался, но так тоже можно.
Визуальный контекст сэкономил кучу времени на объяснении координат и разметки.
Собираем
Raspberry Pi
С прототипом игры разобрались, теперь нужно собрать воедино железную часть.
Телеграфный ключ я заказал на «Авито», это был самый простой тренировочный ключ за 500р.
Когда с авито приехал ключ, схема была собрана и проверена на ардуине из набора «юного ардуинщика», который пылился у меня в шкафу.

Еще спустя две недели всё переехало на RPi 4 model B. Возможностей, в отличие от "ардуины" сильно больше.

Обзора на RPi4 тут не будет, так как единственное, что я сделал, — это установил Raspberry Pi OS на MicroSD-карту и купил переходник micro-HDMI -> VGA.
А еще, конечно, закатал в радиатор, купленый за копейки на известных маркетплейсах.

Мегаомметр
Откуда взялся мегаомметр?
Всё очень просто. Малинку нужно было куда‑то спрятать и при этом сохранить ретро‑вайб, заданный интерфейсом игры.
Для этой цели на всё том же авито был найден и куплен простой советский «есть в каждом доме»... Мегаомметр М1101М

И выпотрошен... 80% места в корпусе занимала динамо-машина.
Зачем мегаомметру динамо-машина?
Мегаомметр используют для измерения высоких значений электрического сопротивления, в основном сопротивления изоляции различных электрических устройств и материалов. Например, кабельной изоляции.
Наш персонаж - классический представитель мегаомметров индукторного типа. Индуктор - он же динамо-машина, предназначен для генерации высоких напряжений (до 2500В).
Такой прибор полностью автономен и не требует внешнего источника питания, что делает его удобным для использования в полевых условиях.
Крутишь ручку — прибор работает, на его щупы при этом подается необходимое напряжение.


Стрелка мегаомметра
Кроме как использовать корпус в качестве «ящика», было решено прицепить замыкание ключа к движению стрелки! Благодаря этому и небольшому совету моей жены — покрасить стрелку неоновым лаком и подсветить УФ‑светодиодами, у нас получился такой ламповый контейнер для «малинки» с интерактивным отображением ввода морзянки. Так выглядит сама стрелка:

А вот так это выглядит в сборе:
Телеграфный ключ
Конечно же не обойдем вниманием и сам телеграфный ключ. Я использовал вертикальный, походного типа.
Изначально я нашёл его на «Мешке» — торговой площадке для коллекционных редкостей. В названии объявления меня привлекла фраза «начало XX века». Как выяснилось позже, ключ оказался с историей.

Ключ попал в коллекцию отца бывшего владельца прямо из стен Куйбышевского сельхозинститута (ныне Самарский ГАУ). Есть веские основания полагать, что этот «малыш» помнит осень 1941 года, когда на территории института располагался штаб 356-й стрелковой дивизии. Скорее всего, именно на таких походных ключах учились связисты‑разведчик��.
Подключаем
Осталось все подключить.
Это та самая доп. платка, на которой уместились два подстроечных резистора. Один регулирует силу отклонения стрелки мегаомметра при замыкании телеграфного ключа, а второй силу отклонения в обратную сторону.

Я планировал один светодиод, и поэтому один резистор размещен на платке, но в последний момент добавилось еще два, которые я подключил к 5В-линии «малинки» напрямую, раскидав потребление между светодиодами с помощью двух резисторов.
Над реализацией подсветки трудились лучшие КБ страны!

Телеграфный ключ также подключен напрямую к «малинке», а его сигнал считывается с помощью библиотеки gpiozero. Для этого используется класс Button:
self.button = Button(self.pin, pull_up=True, bounce_time=self.bounce_time)
Для реализации ввода нам понадобится всего два провода.
Подключать мы их будем в GPIO - это интерфейс, который содержит Входы (Input) и Выходы (Output) "общего назначения" (General Purpose), к которым можно подключать разнообразную периферию:
Один контакт телеграфного ключа соединяем с GPIO 17 (физический пин №11).
Второй контакт ключа соединяем с любым пином GND (например, физический пин №9 или №14).
Поскольку используется pull_up=True, нам не нужно городить огород из внешних резисторов. Процессор сам «подтянет» 17-й пин к 3.3В, а нажатие ключа будет коротить его на землю, что библиотека gpiozero расценит как событие is_pressed.
Распиновка GPIO
Будьте внимательны: не перепутайте номер GPIO (17) с номером физической ножки на разъеме (11). В библиотеке gpiozero по умолчанию используется нумерация BCM (Broadcom SOC channel).

А еще, на платке разместилась общая линия заземления.
В электронике я не шарю, принципиальные схем рисовать не обучен, но подобного визуала ниже и разделения по цветам оказалось достаточно.

Паяем и фиксируем термоклеем критичные соединения, подключаем все согласно схеме и сууууём «малинку» и доп. платку в корпус мегаомметра.
Паял FNiRSi HS-01 - обычный usb-паяльник, способный в нагрев максимум до 300C, чего более чем достаточно.

Эксплуатируем
Шанс опробовать работу "поделки" представился на конференции LINKMEETUP. Это место, где сетевики не только слушают доклады, но и рубятся в мортал комбат, препарируют партнерские стенды, участвуют в тематических викторинах и ломают головы на мастер‑классах.
Оставалось всё это успешно транспортировать.
Мегаомметр вместе с «малинкой» отправился в Новосибирск 30 января и приехал аккурат под митап.
В ночь перед началом всё было собрано. Но во время финальных тестов был найден баг с бонусным временем. Счетчик набранных слов для выполнения условия бонусного времени не сбрасывался после каждой попытки, и был шанс получить +10/+15 сек в зависимости от сложности, например, сразу за первое правильное слово, потому что в предыдущей попытке счетчик накопил 2. Баг был героически поправлен, а код успешно залит на железо вовремя.
На стенде попробовали свои силы 46 человек! Получилось лампово и даже местами азартно.
Итог получился примерно следующий:
Полтора месяца проклятого вайбкодинга и пару часов на исправление бага с бонусным временем в ночь перед митапом
Одна единица старого советского «есть в каждом доме»... мегаомметра с авито
Неоновый лак по совету моей жены и пара УФ светодиодов для модной подсветки стрелки мегаомметра
Довоенный телеграфный ключ с барахолки
Стенд работал с открытия в 10:30 и до 17:00 включительно
Буквально настоящая заруба за первое место на Харде!
46 уникальных участников
35 человек попробовали свои силы на уровне EASY
24 человека на уровне HARD
Это был интереснейший опыт.
Планы
Что дальше?
Обратная связь оказалась кратно выше моих ожиданий, поэтому обязательно повторим на осеннем митапе, задумок накидали целый вагон.
Так же хочу отметить один интересный проект, вышедший в Steam в ноябре прошлого года — Morse.
Как я выяснил, Morse товарищ разработчик делал аж с 2015 года! И в качестве контроллера там так же выступает телеграфный ключ!
Morse вдохновляет попробовать освоить движок Godot, взамен pygame, но это пока не более чем задумка.
Кстати, о Morse я ничего не знал — на проект дали наводку в чате митапа. Но что действительно стало катализатором задумки — стенд с морзянкой на одном из Positive Hack Days.

Бонусом, на митапе у нас было еще две интересных активности.
Доклад
По классике, не обошлось без доклада. Ира Маркова выступала с темой «Почему телефон — это не продолжение телеграфа, а интернет — это не продолжение телефона». Он получился коротким, но преследовал очень важную цель — заинтересовать Вас пойти послушать наши выпуски, количество которых уже перевалило за четвертый десяток!
Панорама по истории связи
На ней присутствует 20 выдающихся личностей, которых участники митапа старались отгадать. Результаты уже выложены, но советую попробовать самостоятельно. Мы старались!
Поразглядывать панораму в высоком разрешении.

Гуглдок по хронологии событий развития связи мы создали почти сразу — в январе 2025-го — и начали накидывать варианты. Стилистику тоже определили на старте. Кстати, не без помощи нейронки. Брейнштормить с ней так же весело, как и с людьми.

А вот вехи мы уже накидывали сами. Отбирали события, личностей, подбирали картинки.

Мы готовили ее к прошлому митапу, но решили придержать до следующего. Мы не успели отдать ее в печать. Панорама требовала полировки. Сейчас можно точно сказать, что это было к лучшему. И вот, спустя n-е количество итераций взаимодействия с прекрасным художником — Артёмом Чернобаем, поиска типухи, печати, подготовки каркаса и контроля всего процесса Маратом, на 8-м линкмитапе это случилось!

А к осеннему митапу мы обязательно придумаем что-то еще.
В рамках подкаста мы уже успели выпустить 4 сезона и сейчас уже начали 5-й. Там мы рассказываем про развитие связи и выдающихся личностях, о победах и поражениях, смелых идеях, патентных войнах, неожиданных союзах и многом, многом другом.
Кстати, послушать наш подкаст можно на следующих платформах:
🎶 Сайт linkmeup
🎶 Youtube
🎶 Яндекс Музыка
🎶 Apple Podcasts
🎶 Spotify
Обратную связь можно нести в чат проекта в телеграм.
