Всё начиналось с идеи
Началось всё в 2024 году в где‑то середине осени. Тогда я ещё не имел тесных дел с arduino и python, но у меня появилась идея сделать простенькую мини клавиатуру для переключения треков, чтобы во время игры во что либо не отвлекался на это. Что‑то около недели и у меня уже получилось собрать на макетке схему со свитчами от клавиатуры, которая управляла громкостью системы и листала треки. Я был в восторге: что‑то, собранное на коленке, тупо с ИИ, работает! Да, тогда я полностью полагался на дипсик или чатГпт.
Затем появился азарт
Далее я захотел сделать хоть какую-нибудь рабочую версию и решил учиться работать в Компас-3D. Сделал я свою первую модель, напечатал, не с первой попытки, как ещё-то, хах. И закончил я как раз к новому 2025 году.

Захотелось чего-то нового
Пользовался я своим творением недолго, где-то месяц, то есть до февраля 2025г. Я решил устроить редизайн, довольно глобальный, сделав ползунковые резисторы, как на верхнем фото, что казалось мне очень удобным и красивым. Я был прав, получилось очень круто! Вот этой версией я пользовался уже где-то до полугода.

Но сытость была не на долго
Я где‑то к лету пару раз успел переписать код на Python, добавляя какие‑то фичи или просто оптимизируя. К осени 2025, я решил снова переделать контроллер, добавив плату, вместо навесного монтажа: не очень всё‑таки удобно каждый раз паять по 20 тонкий проводков, которые обрываются и тому подобное. К сожалению получилось абсолютно не то, чего я ожидал, в том числе потому, что делал я в спешке и даже забыл добавить некоторый функционал, ради которого и делал обновление. Это была какая‑то громадная штука, в пару раз больше чем предыдущая, ну и ваще мне не понравилось :‑(
Усердные попытки дали свои плоды
Но я не сдавался! Сделав снова огромный перерыв, я сделал новую версию, но она не так интересна, как нынешняя версия, в которую я добавил всё, что хотел: и плату, и светодиоды, и наконец... Сам написал код на питоне, который я продолжаю улучшать и по сей день. Сделал я его кстати тоже к новому году, но уже 2026)

Какой путь выберет самурай дальше?
Даже поняв, что мой проект не столь и уникальный, я продолжаю его улучшать и даже уже был первый тестер! На самом деле я хочу сейчас довести проект до условной V‑Beta1.5.0 и чуть выдохнуть, посмотреть на реакцию людей, послушать их мнения. Я начал потихоньку делать посты на пикабу, вомбате, тг. Хочу видос на ютуб ещё снять, но пока не выходит.
Какие изменения за последнее время?
И так. Сейчас я занимаюсь как раз таки доводкой проекта до контрольной точки, а именно:
Делаю плату под новую конфигурацию деталей (добавляю энкодер с кнопками)
Допиливаю код на питоне и для прошивки Arduino Pro Micro
После этого всего моделирую корпус и собираю всё в одно целое
Подробнее про код и прошивку. Для ардуинки добавляю профиль для работы контроллера как MIDI‑устройства. На питоне делаю сейчас GUI для нормальной связи с пользователем, а именно настройки биндов, уведомлений и так далее.


Подробнее про архитектуру проекта
Раньше я писал скрипт вообще в 1 файле, где имелся набор функций и бесконечный цикл. Система была не очень красивая, но работала. Однако, изучив такую вещь как класс в питоне, я смог продвинуться в разработке, и теперь архитектура выглядит вот так.

В целом распределение сделано по функционалу каждого класса, то есть модульным образом. В каждом файле класс, отвечающий за определённую функцию, логику.
? core
-
основные классы, использующиеся либо почти везде:
message — отправление сообщений
config — универсальная настройка логгера
-
либо единожды, для работы некоторых функций программы:
connector — подключение к ардуино
reader — чтение данных с com‑порта
tray_icon — иконка в трее
? data
Все доп файлы для работы программы по типу настроек, иконочек.
# binds { "left_1": "Предыдущий трек", "left_2": "Play / Pause", "left_3": "Следующий трек", "left_4": "Мут звука", "left_A": "Громкость системы", "left_B": "Громкость приложения", "left_C": "Громкость приложения", "left_D": "Громкость приложения", "right_Энкодер": null, "right_↑": null, "right_↓": null, "right_←": null, "right_→": null, "right_●": null }
# notifications { "connection_not": false, "disconnection_not": false, "change_audio_not": false, "bind_not": true, "sound_not": true }
# obs connection settings { "port": "4455", "password": "password", "A": "123", "B": "Звук раб. стола", "C": "Микр/доп", "D": "" }
?️ GUI
Ну, думаю понятно, что gui с настройками уведомлений, биндов (пока не доделал), подключения к obs.
Пока делал пост чуть обновил настройки и теперь вот так это выглядит:

Да, не оч, но я пока не способен сделать что-то сильно лучше. И то прибегал к помощи ии, особенно с вот этой картинкой, там столько объектов спавнить, задолбаешься.
? Handlers
Классы‑обработчики:
default_handler — все методы для переключения музыки, звука и тому подобное
OBS_handler — методы для работы с api obs: подключение, громкость каналов
handler — основной обработчик, где словари с ключами‑командами собираются в 1, и обрабатываются в зависимости от входящих в порт ключей и значений
# словарь методов default_handler self.keys = { 'A': self.master_volume, 'B': self.app_volume, 'C': self.app_volume, 'D': self.app_volume, 'M': self.microphone_state, 'V': self.volume_state, 'K': self.bind, 'P': self.music, 'S': self.music, "N": self.music } # словарь методов OBS_handler self.keys = { 'A': self.set_channel_volume, 'B': self.set_channel_volume, 'C': self.set_channel_volume, 'D': self.set_channel_volume, 'M': None, 'V': None, 'K': None, 'P': self.mute_channel_volume, 'S': self.mute_channel_volume, 'N': self.mute_channel_volume }
А сам класс handler вот:
import logging import time from threading import Thread from Python_code.managers.app_manager import AppManager from Python_code.managers.device_manager import DeviceManager from Python_code.core.reader import Reader from Python_code.core.connector import Connector from Python_code.core.tray_icon import TrayIcon from Python_code.core.message import Message from Python_code.core.config import setup_logger from Python_code.Handlers.default_handler import Default from Python_code.Handlers.OBS_handler import OBS from Python_code.Handlers.containers import Services, Events setup_logger() class Handler: def __init__(self): self.logger = logging.getLogger(self.__class__.__name__) self.tray = TrayIcon() self.msg = Message() self.connector = Connector(self.msg, self.tray.shutdown_event) self.disconnect_event = self.connector.disconnect_event self.app_manager = AppManager(self.disconnect_event, self.msg) self.device_manager = DeviceManager(self.disconnect_event) self.audio_disconnect_event = self.device_manager.audio_disconnect_event self.mic_disconnect_event = self.device_manager.mic_disconnect_event self.reader = Reader(self.connector, self.disconnect_event) self.services = Services( connector=self.connector, reader=self.reader, app_manager=self.app_manager, device_manager=self.device_manager, msg=self.msg ) self.events = Events( audio_disconnect_event=self.device_manager.audio_disconnect_event, mic_disconnect_event=self.device_manager.mic_disconnect_event, disconnect_event=self.disconnect_event ) self.default = Default(self.services, self.events) self.obs = OBS(self.services, self.events) self.keys = { 'A': None, 'B': None, 'C': None, 'D': None, 'M': None, 'V': None, 'K': None, 'P': None, 'S': None, 'N': None } self.path = 'binds.json' self.load_binds() thread = Thread(target=self.process, daemon=True) thread.start() # поток обработки входящих данных def process(self): while True: if not self.disconnect_event.is_set(): key, val = self.reader.wait_for_key(3) if key or val: try: self.keys[key](key, val) self.reader.line = None self.reader.key = None self.reader.val = None except: self.logger.debug(f'На кнопку {key} не назначена команда') time.sleep(0.001) else: time.sleep(3) # пока не сделал( def load_binds(self): pass
?️ managers
Менеджеры, постоянно за чем-то следящие:
-
app_manager — обновляет словарь с аудио приложениями, если появляются новые процессы, плюс переписывает бинды этих приложений в data/mus.json
{ "B": "яндекс музыка.exe", "C": "discord.exe", "D": "hl.exe" } device_manager — следит за аудио устройствами, их подключением, отключением и т. п.
В целом по архитектуре всё. Хочу чуть-чуть, совсем капелльку, объяснить систему того, как это всё работает.
⚙️ Логика
Connector устанавливает соединение с Arduino.
Менеджеры начинают обновлять свои словари и списки из
data/, сканируя систему (аудиоустройства и приложения).Появляется иконка в трее, приходят уведомления о запуске. Работа началась.
С Arduino приходят сообщения вида:
A0.23,N,B0.69и т.п.В handler значения обрабатываются в зависимости от ключа и значения, вызывая нужные методы.
Что дальше?
Проект не закончен, но теперь у него есть твёрдая основа. Дальше — корпус, видео и V‑Beta 1.5.0 как закрепление прогресса.
Спасибо, что дочитали. Я буду рад вашему мнению и идеям — пишите!
Подписывайтесь на мой telegram-канал, там я рассказываю об изменениях подробнее.
Комментарии (10)

KirOFF_YT
26.05.2026 12:36Другое название: "Как сделать StreamDeck в домашних условиях?" :)

FRIMID Автор
26.05.2026 12:36Ну дааа это не что-то прорывное, между прочим я писал в посте, что не прям оригинальная эта штука. Мне просто интересно делать, разбираться в этом всём, получать опыт. Хотел поделиться и может найти поддержку.

NutsUnderline
26.05.2026 12:36у меня в закладках есть пара подобных клавиатур с энкодерами очень недорогие. и без фейдеров. с фейдерами это уже скорее микшеры

FRIMID Автор
26.05.2026 12:36Можете поделиться пожалуйста? Я бы посмотрел, может что-то улучшил у себя в проекте. А микшер это вроде уже должна быть типа звуковая карта, чтобы именно менять звук устройства, а тут немного другое.

NutsUnderline
26.05.2026 12:36сбросил в личку. у них даже нормального названия нету
для звука есть и звуковые которые больше микшер специализированный, как раз для стримингов. но есть еще вариант с кучей падов, крутилок и фейдеров и часто но не всегда с музыкальнйо клавиатурой - чисто внешний контроллер с протоколом midi для синтезаторов но и не только. можно светом сценическим управлять. и вообще любым софтом который умеет пhотокол midi включая obs. есть шлюзы в другие протоколы.

Orved
26.05.2026 12:36Круто! Т.е. я могу запилить на каждый слайдер звук определенного приложения? Нет ложных срабатываний от арду потенциометров?

FRIMID Автор
26.05.2026 12:36Ага, можно удобно биндить приложения, я изначально для этого и задумывал. Надо нажать на кнопку, включится режим "бинда", затем открыть нужное окно и подвигать нужный потенциометр. Теперь он будет всегда управлять громкостью этого приложения, пока не поменять бинд. Ложных срабатываний нет, так как сама ардуинка считывает значения с них в диапазоне 0-1023, а я преобразую 0-100. Так сто у каждого значения довольно широкий диапазон.
FRIMID Автор
Вот ссылка на github: https://github.com/FRIMIDOVO/Miksher-Releases.git