Хабр, привет. Это мой первый пост — сильно камнями не кидаться.
Цель: собрать максимально бюджетный макропад и изучить технологии, применяемые для его сборки.
Требования: минимальный бюджет, количество клавиш — минимум 10, механический тип клавиатуры, удобная эргономика, полное понимание внутренних процессов, возможность смены прошивки и назначения для клавиш своих функций. Ну и хочется это сделать быстро, не за 3 месяца.
Макропад — это компактная программируемая мини‑клавиатура с небольшим количеством клавиш, предназначенная для автоматизации рутинных задач путем назначения на каждую кнопку различных команд, горячих клавиш или макросов (последовательностей действий).

Изучив рынок маркетплейсов, китайских магазинов и б\у техники, я обнаружил для себя несколько проблем:
Цена за такие огрызки клавиатур несоизмеримо большая, начиная от 1000₽ для приемлемых вариантов.
Бюджетные варианты имеют малое количество клавиш и\или не имеют возможности прошивки и настройки нажатия клавиш.
Неизвестно, имеет ли устройство возможность настройки нажатия клавиш и изменения прошивки. И есть вероятность в случае потери\удаления прошивки превратить устройство в камушек, т.к. не факт, что получится найти нужную прошивку для него.
Не все устройства имеют удобную эргономику.
Покопавшись в интернетах, посмотрев ролики-туториалы и посоветовавшись с искусственным интеллектом, я понял, что собрать свое устройство не так уж и сложно.
Я написал список материалов и действий:
Корпус.
Плата-контроллер.
Свитчи для механической клавиатуры.
Кейкапы.
Спаять все это дело.
Запрограммировать.
Собрать в корпус.
Я решил не использовать плату для свитчей из-за ее цены (да и найти ее или сделать под случайно найденный корпус довольно проблематично), а спаять их напрямую с помощью медной проволоки.
Так же можно было вытравить плату самому, что вышло бы довольно бюджетно и свитчи сидели бы крепко. А еще можно было бы использовать сокеты для свитчей на плате, это электронный компонент припаиваемый на плату, который позволяет просто вставлять свитч в гнездо без пайки, и можно было бы их быстро и просто поменять. Ну и имея плату можно было впаять RGB светодиод под каждый свитч, что добавило бы яркую и полностью управляемую подсветку.

Комплектующие
Корпус будет печататься на 3D-принтере, в интернете можно найти бесплатные модели или сделать самому. Я нашел первый попавшийся мне удобный корпус на 10 клавиш. Печать небольшого корпуса мне обошлась в ~150₽, заказал услугу у частника по объявлению на маркетплейсе. Печать вышла почти по себестоимости, но некоторые частники и конторы могут сильно завышать цену для своей выгоды.


Сюда можно установить 10 клавиш, и здесь довольно неплохая эргономика под левую руку, минимум движений кистью.
Для платы контроллера я выбрал китайский вариант Rapsberry Pi Pico Black c Type-C разъемом и 4 МБ памяти на борту. И еще она имеет встроенный RGB светодиод, что позволит добавить подсветку для устройства. 150₽, заказал с китайского популярного маркетплейса.
Есть ряд контроллеров которые стоят еще дешевле и имеют меньший размер, например ESP32 S2 Mini. А так же есть контроллеры с встроенными модулями WiFi\BT на плате, и с добавлением аккумулятора и контроллера зарядки можно сделать устройство беспроводным.
Свитчи я брал самые бюджетные MX, мне было все равно на их тип, качество, шум и цвет.
Вышло по 10₽ за шт * 10 = 100₽. Заказал с китайского популярного маркетплейса.
Кейкапы лично я для себя не брал, использовал со своей старой клавиатуры, но цена самых дешевых, также ~10₽ за шт * 10 = 100₽. Заказать можно с китайского популярного маркетплейса.
Кабель Type-C с передачей данных может тоже выйти в районе 100₽.
Суммарно 600₽.
Также нам понадобится паяльник с расходниками, медная проволока, провода и прямые руки.
Пайка
У меня 10 кнопок, на каждой по два контакта, если паять их напрямую линейно к плате, то мы займем 20 пинов. Данная плата это позволяет, но я решил использовать матрицу кнопок. Это когда у тебя есть матрица из проводов, состоящая из рядов и колонок, каждое пересечение ряда и колонки — это одна кнопка, ее замыкание вызывает протекание тока с определенного ряда и на определенную колонну. С помощью номеров этих рядов и колонн мы сможем понимать, какая кнопка нажата. Например, у меня 10 кнопок, матрица 3х3 будет маленькой, берем 3х4 — это 3 ряда и 4 колонны. В сумме получаем 7 контактов на плате вместо 20. Неплохо ужались.
По‑хорошему нужна диодная защита, это когда при зажатии нескольких кнопок одновременно может возникнуть ситуация, при которой ток течёт через неожиданные пути. Это происходит из‑за того, что без диодов все линии (ряды и колонки) электрически соединены через зажатые кнопки, что может «зажечь» дополнительные кнопки, которых ты не нажимал. Но для себя я этого не делал.
После пайки матрицы припаиваем провода от каждого ряда и колонки на пины платы.
Я паял колонки на пины 1, 2, 4, 6. Ряды — на 3, 5, 7.
Для себя я также использовал два светодиода для подсветки на пинах 8 и 9.
Если кто‑то столкнется с такой платой, то знайте: изначально встроенный светодиод на ней не подключен к плате. Светодиод имеет пин 23, который изначально работает как обычный пин. Чтобы подключить светодиод к нему, надо спаять перемычку на плате, где написано «RGB» или «R68». Я долго искал эту информацию.



Программирование
Я пробовал разные IDE, прошивки, языки программирования и библиотеки. Сначала я пытался использовать PlatformIO, но он не видел мою плату после прошивки. Пытался использовать С++ для программирования, но так и не разобрался, как ставить библиотеки. После недолгого сражения с софтом мы с ИИ смогли найти решение.
Использовать программу Thonny, она довольно простая, поддерживает установку библиотек и может автоматически установить прошивку.
Использование языка программирования Python. Проще, чем C++, и имеет много библиотек.
Использовать прошивку от CircuitPython, у меня таким образом можно получить доступ к внутренней памяти и файлам, просто подключив устройство к ПК. В отличие от MycroPython, где я не могу получить обычный доступ к внутрянке.
Я использовал библиотеку usb_hid от CircuitPython, которая позволяет эмулировать нажатия клавиш на клавиатуре.
Я набросал для себя часто используемые клавиши и комбинации, и понял, что 10 кнопками не обойдусь. Я решил использовать 2 слоя на макропаде. Первый слой — это обычные нажатия клавиш, второй слой — это нажатия на клавиши с уже нажатой клавишей слоя. Это позволяет увеличить кол-во возможных комбинаций с 10 до 18.
Вот такая раскладка вышла:

Первая строка в каждой клавише — это первый слой, вторая строка — это второй слой.
Кнопка 9 — переключение слоя, кнопка 10 на втором слое — переключение подсветки.
Мой код. Сгенерированный ИИ.
import time
import board
import digitalio
import pwmio
import usb_hid
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
import config
# Пины для CircuitPython
ROWS = [digitalio.DigitalInOut(getattr(board, f"GP{i}")) for i in [3, 5, 7]]
for row in ROWS:
row.switch_to_output()
COLS = [digitalio.DigitalInOut(getattr(board, f"GP{i}")) for i in [1, 2, 4, 6]]
for col in COLS:
col.switch_to_input(pull=digitalio.Pull.UP)
# Инициализация клавиатуры
kbd = Keyboard(usb_hid.devices)
# Состояние слоя
current_layer = config.layer1 # По умолчанию первый слой
layer_switch_button = 9 # Кнопка для переключения слоя
# Список для хранения текущего состояния нажатий и времени
current_presses = {} # {кнопка: время_зажатия}
last_presses = set()
# Параметры автоповтора
INITIAL_DELAY = 0.5 # Начальная задержка перед повтором (0.5 секунды)
REPEAT_DELAY = 0.03 # Интервал повтора (30 мс)
# Управление подсветкой
led1 = pwmio.PWMOut(board.GP8, frequency=1000) # Первый цвет (минус на GP8)
led2 = pwmio.PWMOut(board.GP9, frequency=1000) # Второй цвет (минус на GP9)
led1.duty_cycle = 0 # Начальное значение (выкл)
led2.duty_cycle = 0 # Начальное значение (выкл)
# Режимы подсветки (0-4)
LIGHT_MODES = [
lambda: (65535, 65535), # 0: Всё горят (GP8, GP9)
lambda: (65535, 0), # 1: Первый горит (GP8)
lambda: (0, 65535), # 2: Второй горит (GP9)
lambda: (0, 0), # 3: Всё выкл (GP8, GP9)
lambda: (0, 0) # 4: Перелив (обновляется асинхронно)
]
current_mode = 0
# Ограничение значений duty_cycle
def clamp_duty(duty):
return min(max(int(duty), 0), 65535)
# Сканирование матрицы
def scan_matrix():
global current_presses, last_presses, current_layer, current_mode
current_time = time.monotonic()
# Сканирование текущего состояния
new_presses = set()
for i, row in enumerate(ROWS):
row.value = False
for j, col in enumerate(COLS):
if not col.value:
btn_num = i * len(COLS) + j + 1
if i == 2 and j > 1:
if j == 2: btn_num = 9
elif j == 3: btn_num = 10
new_presses.add(btn_num)
row.value = True
# Обновление состояния нажатий
for btn in new_presses:
if btn not in current_presses:
current_presses[btn] = current_time
if btn in current_layer:
kbd.send(*current_layer[btn])
print(f"Отправлено: {btn} -> {current_layer[btn]}")
# Автоповтор
for btn in list(current_presses.keys()):
if btn in new_presses:
press_time = current_time - current_presses[btn]
if press_time > INITIAL_DELAY and (press_time - INITIAL_DELAY) % REPEAT_DELAY < 0.005:
if btn in current_layer:
kbd.send(*current_layer[btn])
print(f"Повтор: {btn} -> {current_layer[btn]}")
else:
del current_presses[btn]
# Переключение слоя при зажатии 9
if layer_switch_button in new_presses and layer_switch_button not in last_presses:
current_layer = config.layer2
print("Переключено на второй слой")
elif layer_switch_button not in new_presses and layer_switch_button in last_presses:
current_layer = config.layer1
print("Вернулись на первый слой")
# Смена режима подсветки при нажатии 10 только на втором слое
if 10 in new_presses and 10 in current_layer and current_layer == config.layer2 and 10 not in last_presses:
current_mode = (current_mode + 1) % len(LIGHT_MODES)
duty1, duty2 = LIGHT_MODES[current_mode]()
led1.duty_cycle = clamp_duty(duty1)
led2.duty_cycle = clamp_duty(duty2)
print(f"Режим подсветки: {current_mode} (duty1={duty1}, duty2={duty2})")
last_presses = new_presses.copy()
# Обновление перелива для режима 4
def update_fade():
if current_mode == 4:
value = int(65535 * abs(0.5 - (time.monotonic() % 2) / 2))
led1.duty_cycle = clamp_duty(value)
led2.duty_cycle = clamp_duty(65535 - value)
# Основной цикл с обработкой прерываний
try:
while True:
scan_matrix()
update_fade() # Асинхронное обновление перелива
time.sleep(0.01) # Уменьшаем частоту обновления для плавности
except KeyboardInterrupt:
print("Программа прервана пользователем")
И содержание файла с конфигом config.py
from adafruit_hid.keycode import Keycode
# Первый слой
layer1 = {
1: [Keycode.ESCAPE], # Escape
2: [Keycode.TAB], # Tab
3: [Keycode.UP_ARROW], # Стрелка вверх
4: [Keycode.BACKSPACE], # Backspace
5: [Keycode.DELETE], # Delete
6: [Keycode.LEFT_ARROW], # Стрелка влево
7: [Keycode.DOWN_ARROW], # Стрелка вниз
8: [Keycode.RIGHT_ARROW], # Стрелка вправо
10: [Keycode.ENTER] # Enter
}
# Второй слой (активируется при зажатии 9)
layer2 = {
1: [Keycode.CONTROL, Keycode.A], # Ctrl+A
2: [Keycode.CONTROL, Keycode.X], # Ctrl+X
3: [Keycode.CONTROL, Keycode.C], # Ctrl+C
4: [Keycode.CONTROL, Keycode.V], # Ctrl+V
5: [Keycode.CONTROL, Keycode.ALT, Keycode.DELETE], # Ctrl+Alt+Del
6: [Keycode.CONTROL, Keycode.S], # Ctrl+S
7: [Keycode.CONTROL, Keycode.Z], # Ctrl+Z
8: [Keycode.CONTROL, Keycode.Y], # Ctrl+Y
10: [] # Смена режима подсветки
}
Ну и автоповтор добавил, чтоб при зажатии кнопочки она повторяла свои действия в быстром режиме.
Подключаем, прошиваем, заливаем код, и после танцев с бубном оно заработало!
Собираем все в корпус, ставим красивые кейкапы и готово.


На пайку и программирование ушло 2 вечера.
Итог: я смог быстро собрать бюджетный макропад с 10 кнопками, удобной эргономикой, легко изменяемым конфигом. И оно работает при подключении к любому ПК.
Следующая цель - самодельная эргономичная сплит клавиатура.
Комментарии (10)
dvmuratov
07.10.2025 12:56На ESP32 S2 Mini еще и дешевле вышло бы.
А для свитчей можно было плату вытравить и контроллер прям на нее. Тогда вообще бы крепко вышло.
RawFlash Автор
07.10.2025 12:56Согласен, есть ряд контроллеров дешевле и проще, есть и с модулями беспроводной связи. Добавить аккум и контроллер зарядки, и можно было бы и беспроводной вариант получить.
Да, думал об самодельной плате, думал даже о том чтоб использовать сокеты для свитчей. Тогда можно было бы не паять свитчи вообще, а просто вставлять их, и менять на любые свитчи.Tony-Sol
07.10.2025 12:56ИМХО, вместо kailh с картинки, лучше сокеты mill-max 3305-1. Да, будет подороже, но они переживут больше замен свичей.
nem0ff
07.10.2025 12:56Для подключения 10 кнопок индивидуально, без использования матрицы, надо не 20 контактов, а 10.
На всякий случай конкретизирую :) Один контакт всех кнопок подключается к шине питания или земли.
RawFlash Автор
07.10.2025 12:56Абсолютно верно, я и не подумал об этом. Но матрицей все равно меньше пинов выходит, хоть и фантомные замыкания вызывает. Спасибо за уточнение, статью обновлю)
IDma88
07.10.2025 12:56Фантомные замыкания лечатся диодами. Подробнее можно почитать тут https://habr.com/ru/articles/394585/
vis_inet
07.10.2025 12:56А если купить что-то готовое, кто-нибудь пользуется уже или искал, подбирал?
nicolas_d
ок... бюджет, все дела. Но, как всегда, дьявол кроется в деталях. Очень похоже на гайды по созданию табуретки подручными средствами, которые есть в гараже. Открывается гараж, а там оборудования столярного и токарного на пару лямов баксов. Вот к примеру, ну нет у меня 3D принтера. Да, я могу заказать печать разработанного мной корпуса, но, в результате, итоговая сумма вырастет кратно. Никто за бесплатно корпус печатать мне не будет.
Это так, брюзжание в общем. В целом же, очень неплохо, я тоже рассматривал вариант сделать самому программируемую мини клавиатуру, но в итоге купил готовую.
RawFlash Автор
Согласен, мой корпус почти по себестоимости вышел. Возможно некоторые конторы и частники могут сильно завысить цену на печать.
В будущем времени хочу попробовать эргономическую клавиатуру для работы, и сейчас менжуюсь - сделать самому или взять готовую. Склоняюсь к первому варианту, если эксперимент удастся, то напишу еще один пост)