Когда сын подкопил денег и твёрдо решил купить себе Яндекс Станцию с Алисой, я сразу понял: в этом случае обязательно попробую что-нибудь с ней попрограммировать.
Во-первых, в коммерческой разработке я почти не трогал голосовые интерфейсы (VUI — Voice User Interface). А тут можно побыть прикладным разработчиком не только под мониторы и браузеры.
Во-вторых, голосовые помощники часто ассоциируются с «умным домом»: они оркестрируют всё, что к ним можно подключить. То есть тут и интернет вещей (IoT — Internet of Things): связка датчик → сервер → умная колонка, — и повод потрогать браузерные API для железок вроде Web Serial, WebUSB и Web Bluetooth, которые в обычной работе пока не пригождались.

Яндекс Диалоги — стор навыков Алисы
У Яндекс Станции есть что-то вроде стора приложений — Яндекс Диалоги. Туда можно публиковать навыки Алисы публично (доступно всем) или приватно: навык живёт только на вашей колонке под вашим аккаунтом.
У Алисы есть протокол общения с бэкендом. Голосовая реплика превращается в JSON и прилетает на указанную вами серверную точку. Сервер отвечает по тому же протоколу — текст, TTS, кнопки, флаг «закончить сессию» — и станция уже превращает ответ в речь.
Простым языком: это связка станция → интернет → сервер.
В личном кабинете создаёте навык, указываете HTTPS-эндпоинт — и станция начинает слать туда запросы.

Первая проба — голосовая игра про Ведьмака
Как-то сын заскучал в гостях у бабушки, и я решил: пора сделать первое приложение-игру.
Концепция простая и опирается на «Ведьмака» Сапковского. Запускаешь навык, выбираешь ведьмачью школу и попадаешь в игровой цикл: Алиса подкидывает нового монстра, а ты решаешь, чем его победить — серебряным мечом или зельем вербены.
Ничего сложного. Вместе с ИИ-агентом быстро разобрались с протоколом Алисы, собрали данные по монстрам, написали логику на эндпоинте этого же сайта и задеплоили. Потом скормили URL навыку и опубликовали.
В кабинете Диалогов есть тестовый чат: голосовое общение заменяется текстом — удобно для отладки. Я скопировал этот подход и сделал страницу у себя прямо на сайте, где навык можно гонять онлайн без колонки: arturbasak.dev/yandex-alice-skills.

После публикации можно запускать игру голосом. Восторг! Голосовая игра работает.
Но хочется пойти дальше: чтобы в связке станция → интернет → сервер появился ещё и сенсор — ну прямо как в реальном умном доме.
Следующий шаг — навык для micro:bit
Я уже раньше программировал с детьми под micro:bit. Они в теме этой «игрушки», прошивать визуальными блоками через MakeCode умеют. Мысленно замените микроконтроллер на робот-пылесос, холодильник или стиральную машину — вот вам и умный дом.
Мы сделали навык «Мой Microbit»: Алиса может сказать «улыбнись», «издай звук», показать грусть или прокрутить текст — а сервер превращает это в короткую текстовую команду. По сути второй протокол поверх первого: станция → сервер → микроконтроллер.
Навык собирался по аналогии с Ведьмаком. Оставался пробел: как с сервера реально достучаться до платы.
Кабель: UART по USB
У micro:bit нет Wi‑Fi, в интернет сам он не выходит. Зато есть USB — и по кабелю работает UART (Universal Asynchronous Receiver/Transmitter): простой последовательный канал «байт за байтом» без сложной адресации. Для нас это виртуальный COM-порт (притворяемся старым физическим толстым кабелем-переводчиком) на скорости 115200 бод (бит в секунду).
В документации платы лежат USB Vendor ID / Product ID — фильтры, по которым браузер узнаёт именно micro:bit среди кучи USB-устройств (0x0D28 / 0x0204). ИИ-агент их быстро нашёл и копаться самому руками в документации почти не пришлось.
Через Web Serial (а при отсутствии Serial — через WebUSB) браузер может открыть порт и писать туда наши команды:
const port = await navigator.serial.requestPort({
filters: [{ usbVendorId: 0x0d28, usbProductId: 0x0204 }],
});
await port.open({ baudRate: 115200 });
const writer = port.writable.getWriter();
await writer.write(new TextEncoder().encode('SMILE\n'));Одна строка — одна команда. SMILE, SAD, HEART, BEEP, CLEAR, TEXT:привет — всё заканчивается \n.
Я сделал страницу-коннектор: из браузера подключаемся к плате и шлём свой текстовый протокол, примерно как MakeCode, только со своими глаголами — arturbasak.dev/microbit-connector.
Мост сервер → страница: WebSockets
Остаётся вопрос: как серверу, который получил команду от Алисы отправить ее страничке, которая по USB или Bluetooth общается с платой? Сервер сам по себе к micro:bit не подключится — ему нужно как-то достучаться до страницы-коннектора.
Для этого мы используем WebSockets: постоянный двусторонний канал между сервером и открытой вкладкой. Конкретно — Pusher Channels: эндпоинт Алисы получает голос, понимает команду (улыбнись → SMILE) и публикует событие в канал. Вкладка коннектора уже подписана на этот канал по WebSocket, ловит сообщение и пишет байты в UART — по USB или по BLE.
На самой странице это видно глазами: рядом со статусом платы есть блок «Мост Алисы». Когда всё ок, там не только «micro:bit подключен», но и статус вроде «Слушает» — значит, вкладка на связи с облаком по WS через Pusher и готова принимать команды со Станции. Без открытого коннектора голос Алисы дойдёт до сервера, но до светодиодов — нет.
Цепочка целиком: Станция → интернет → сервер навыка → WebSocket (Pusher) → вкладка коннектора → UART/BLE → micro:bit.
Ок, но плата на кабеле к ноутбуку — всё равно негибко. Хочется показать детям беспроводной путь до самой железки: как с телефона или со Станции запускают пылесос.
Без провода: Bluetooth, GATT, RX/TX
У micro:bit нет Wi‑Fi, зато есть Bluetooth Low Energy. По BLE устройство выставляет GATT-сервисы (Generic Attribute Profile) — по сути каталог «что умеет эта железка». Внутри сервиса лежат характеристики: поля, в которые можно писать или из которых читать байты.
Для UART поверх Bluetooth micro:bit использует Nordic UART Service (NUS). Важно не перепутать направление:
- RX (Receive с точки зрения платы) — характеристика, куда браузер пишет команды (
…003) - TX (Transmit с точки зрения платы) — характеристика, откуда браузер читает ответы (
…002)
То есть «RX для платы» = «писать из Chrome». Путаница привычная, в даташите Nordic так и заведено.
Через Web Bluetooth шлём те же UART-строки, только по воздуху:
const device = await navigator.bluetooth.requestDevice({
filters: [{ namePrefix: 'BBC micro:bit' }],
optionalServices: ['6e400001-b5a3-f393-e0a9-e50e24dcca9e'],
});
const server = await device.gatt.connect();
const service = await server.getPrimaryService('6e400001-b5a3-f393-e0a9-e50e24dcca9e');
const rx = await service.getCharacteristic('6e400003-b5a3-f393-e0a9-e50e24dcca9e');
await rx.writeValue(new TextEncoder().encode('SMILE\n'));Транспорт готов. Но есть пробел: прошивка пока не понимает ни USB-, ни BLE-команд.
Сын пишет прошивку в MakeCode
И вот тут в дело вступает сын. В MakeCode он собирает функцию handleCommand: пришла строка SMILE — рисуем улыбку, BEEP — короткий звук, TEXT:… — бегущая строка. Два обработчика — на последовательный порт и на Bluetooth UART — вызывают одну и ту же логику. Всё блоками, на визуальном языке.

Схема на TypeScript из той же логики выглядит так (лаконично, как в доке):
bluetooth.startUartService();
function handleCommand(cmd: string) {
if (cmd == 'SMILE') {
basic.showIcon(IconNames.Happy);
} else if (cmd == 'SAD') {
basic.showIcon(IconNames.Sad);
} else if (cmd == 'BEEP') {
music.play(music.tonePlayable(Note.C, 100), music.PlaybackMode.InBackground);
} else if (cmd.substr(0, 5) == 'TEXT:') {
basic.showString(cmd.substr(5));
}
}
serial.onDataReceived(serial.delimiters(Delimiters.NewLine), function () {
handleCommand(serial.readUntil(serial.delimiters(Delimiters.NewLine)));
});
bluetooth.onUartDataReceived(serial.delimiters(Delimiters.NewLine), function () {
handleCommand(bluetooth.uartReadUntil(serial.delimiters(Delimiters.NewLine)));
});Прошиваем micro:bit, открываем коннектор, подключаем USB или Bluetooth, запускаем навык на Станции — и плата послушно рисует улыбки и пищит по голосу.
На мой взгляд, получилась солидная лабораторная работа: VUI, JSON-протокол Алисы, WebSockets-мост через Pusher, свой командный протокол, Web Serial / Web Bluetooth, GATT и детский MakeCode в одном контуре.