ESP32-S3 Bench Guide
ESP32-S3 N16R8 bench guide: live pinout, lessons, simulators and the bench log.
Интерактивная голосовая сказка и уроки для детей 3-7 лет на казахском с русским переводом
Translation pending
AI summary · revised during review
EducationJavaScript · HTMLGitHub @MAL-AuylOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Интерактивная голосовая сказка: ребёнок 3–7 лет отвечает голосом и влияет на сюжет, реплики на казахском с русским подстрочником. Подробнее об идее — IDEA.md. Открыть http://localhost:3000 — это библиотека (выбор сказки или урока); сама сказка — http://localhost:3000/story.html. Панель оператора (скрыта от ребёнка) — клавиша `, значок ⚙ в шапке, либо параметр ?op=1 в URL (/story.html?op=1).Read the full README ↗
Интерактивная голосовая сказка: ребёнок 3–7 лет отвечает голосом и влияет на сюжет, реплики на казахском с русским подстрочником. Подробнее об идее — IDEA.md.
| Каталог | Назначение |
|---|---|
public/ | детское приложение (статика), деплоится Vercel: index.html — библиотека (главный экран), story.html — сказка/урок |
api/ | Vercel Edge functions (transcribe.js, classify.js) |
server/ | локальный Bun-сервер: server.js, bins.js |
lib/ | модули без DOM/Bun-зависимостей (blocklist, STT-подсказки, выбор транскрипта, классификатор, Origin-guard) |
tools/ | prerender.py, dump-story.js, requirements.txt |
tests/ | bun test |
docs/ | сценарий, статус-отчёты, спеки |
cp .env.example .env # вписать GROQ_API_KEY
bun server/server.js
Открыть http://localhost:3000 — это библиотека (выбор сказки или урока); сама сказка —
http://localhost:3000/story.html. Панель оператора (скрыта от ребёнка) — клавиша `,
значок ⚙ в шапке, либо параметр ?op=1 в URL (/story.html?op=1).
Без GROQ_API_KEY и/или без ffmpeg/whisper/piper приложение всё ещё работает: живой STT и
live-TTS отключаются, но заранее пре-рендеренные .wav в public/audio/ и ручные кнопки
оператора (✅/🔁/⏭) позволяют пройти историю без микрофона.
Порядок разрешения бинарей: явный env → PATH → tools/.venv (для PIPER_BIN — сначала
tools/.venv, затем PATH) (см. server/bins.js).
| Переменная | По умолчанию | Если не задана/не найдена |
|---|---|---|
GROQ_API_KEY | — | нет STT (Whisper) и LLM-классификатора ответов |
GROQ_STT_MODEL | whisper-large-v3 | — |
STT_LANGS | auto,ru | — |
ALLOWED_ORIGIN_EXTRA | — | API на Vercel принимает только *.vercel.app / localhost; свой домен нужно перечислить здесь через запятую (ertegim.kz,*.ertegim.kz), иначе все запросы получат 403 (см. lib/origin-guard.js) |
GROQ_TIMEOUT_MS | 8000 локально, 12000 на Vercel | — |
PORT | 3000 | сервер слушает 3000 |
FFMPEG_BIN | ffmpeg из PATH, иначе бинарь imageio-ffmpeg из tools/.venv | нет loudnorm-препроцессинга и pitch-shift для TTS, локальный Whisper недоступен |
WHISPER_BIN | whisper-cli из PATH | только Groq STT |
WHISPER_MODEL | tools/models/ggml-small.bin, если существует | только Groq STT |
PIPER_BIN | tools/.venv/bin/piper (Windows: tools/.venv/Scripts/piper.exe), иначе piper из PATH | /api/speak отвечает 503, клиент переключается на пре-рендеренные .wav |
PIPER_VOICE_KK | tools/voices/kk_KZ-issai-high.onnx, если существует | как выше |
python3 -m venv tools/.venv
tools/.venv/bin/pip install -r tools/requirements.txt
tools/.venv/bin/python tools/prerender.py
Скачает голос Piper в tools/voices/ (с HuggingFace; если он недоступен в вашей сети — тот же
голос берётся из GitHub-релиза rhasspy/piper v0.0.2) и озвучит все реплики истории и уроков
в public/audio/. --force — переозвучить всё заново; --only id1,id2 — только перечисленные
реплики. Реплики уроков (a_*, q_a_*) рендерятся медленнее (LESSON_LENGTH_SCALE), как
просит docs/lesson-letter-a.md.
bun test
Распознавание идёт в два прохода: один и тот же клип отправляется в Groq Whisper
дважды параллельно — по одному вызову на каждый элемент STT_LANGS
(по умолчанию auto,ru), — а lib/stt-pick.js выбирает тот
результат, который лучше отвечает на вопрос узла (счёт, развилка, рифма,
поддержка), отбрасывая галлюцинации Whisper («Субтитры сделал DimaTorzok») и
помечая тихие/неуверенные записи как lowConfidence — тогда сказка мягко
переспрашивает, не дёргая LLM.
auto,ru и whisper-large-v3Раньше проходы были kk,ru на whisper-large-v3-turbo. На живом тесте с
телефона реальные казахские ответы возвращались либо пустыми, либо уверенной
казахоподобной бессмыслицей («үңі ғыңңқ») — turbo с принудительным
language=kk заставляет модель «договорить» фонетику низкоресурсного языка
вместо честного низкого confidence. Это известный режим отказа, и он хуже
пустого ответа: галлюцинация выглядит как уверенное распознавание и идёт
дальше по пайплайну.
Поэтому по умолчанию: полная модель whisper-large-v3 (точнее turbo на
низкоресурсных языках; задержка не критична — TTS/микрофон и так занимают
8-12 с под анимацией «думаю») и auto вместо kk — язык определяет сам
Whisper, а словарная подсказка (lib/stt-hints-core.js) и
isMostlyCyrillic() остаются. Каждый вопрос в story.js и так принимает
ответ на казахском или русском, так что заставлять модель выбирать
более сложный язык незачем.
STT_LANGS=kk,ru возвращает прежнее поведение (принудительный казахский),
STT_LANGS=auto — один проход. Порядок важен: при равном счёте
lib/stt-pick.js выбирает первый в списке.
Цена: два прохода — это два вызова Groq STT на каждый ответ ребёнка (вдвое
больше запросов и стоимости, чем при одном проходе); пороги noSpeechProb 0.6 и
avgLogprob −1.2 в lib/stt-pick.js взяты «на глаз» и не настроены — их и надо
откалибровать прогоном лаборатории на реальных детских записях.
Чтобы настроить это на реальных детских записях, есть локальная страница
(только bun server/server.js, на Vercel её нет):
GROQ_API_KEY=... bun server/server.js
# открыть http://localhost:3000/lab.html
Выбрать записи детей, узел (q_tracks / q_fork / q_courage / q_echo), для
счёта — trackCount, при необходимости поправить langs (подставляется из
STT_LANGS сервера — так и сравнивают kk,ru против auto,ru против auto),
вписать ожидаемый ответ напротив каждого файла и нажать «Run turbo» / «Run v3».
Таблица покажет оба транскрипта (с пометкой «запрошено→распознано», чтобы было
видно, что именно определил auto), выбранный, score, confidence и совпадение
с ожидаемым; внизу — точность и среднее время на файл для каждой модели.
Vercel zero-config: public/ как статика, api/ как Edge functions. В настройках проекта
на Vercel задать переменную GROQ_API_KEY. Live TTS (/api/speak) на Vercel недоступен —
используются только пре-рендеренные .wav. На Vercel также нет ни живого TTS, ни
LLM-классификатора (/api/classify есть только у локального сервера) — ответ ребёнка
оценивает локальный классификатор в браузере (classify-local.js).
public/voices.html — dev-страница для подбора голоса Piper, работает только с локальным
сервером.
VAD_START_RMS
(0.035) измерен на телефоне до появления ограничений
noiseSuppression/autoGainControl, их надо перемерить в /lab.html.STT_LANGS: auto,ru против kk,ru на детских записях — по одному вызову
Groq на проход, так что сравнение стоит денег.q_fork, q_courage, echo_reveal, thanks) — носителем языка.Кроме первой сказки «Түлкі інісін іздейді», на главной есть ещё три, каждая на 4–5 минут и с пятью речевыми играми:
?lesson=bear-honey): согласиться помочь медведю, пожужжать «ж-ж-ж», посчитать
пчёл, найти мёд среди картинок, сказать «рақмет».?lesson=owl-star): поздороваться «сәлем», позвать по-совиному
«у-у-у», посчитать звёзды, найти звёздочку, загадать желание.?lesson=fox-birthday): поздравить лисёнка, посчитать шарики, найти
красный, задуть свечки «ф-ф-фу» (упражнение на выдох), придумать подарок.Сказки — это данные в реестре TALES (public/story.js): полка, наклейки, отчёт, кабинет,
серверное распознавание (lib/tale-forms.js, генерируется node tools/tale-forms.js) и озвучка
подхватывают новую сказку сами. Как добавить сказку и все реплики для вычитки —
docs/tales.md.
У каждого героя может быть свой голос: tools/prerender_voicestudio.py озвучивает лисёнка, сову и
медведя разными голосами локальной моделью VoiceStudio (OmniVoice), выравнивает громкость под Piper и
перерисовывает только изменённые реплики. Пошагово для Windows — docs/voice/voicestudio.md.
Сказка играет готовый файл реплики раньше живого синтеза, поэтому выбранные голоса звучат и на сайте,
и на локальном сервере. Лицензия OmniVoice некоммерческая: для пилота можно, для платного продукта — Piper.
Кроме сказки, та же страница story.html запускает короткие уроки букв для детей с
задержкой речи, дизартрией, РАС: story.html?lesson=letter-a (карточка «А әрпі — түлкімен» на
главном экране index.html). Урок «Буква А» (3–5 минут) описан в docs/lesson-letter-a.md
и реализован как LESSON_A + ACTIVITIES в public/story.js:
mode: "imitate" — ребёнок повторяет звук/слово за героем; принимаются детские искажения
(accept), любое слово на нужный звук (acceptPrefix), заикание и растяжка схлопываются
(destutter — одинаково в public/classify-local.js и lib/stt-pick.js, тест на паритет);mode: "pick" — две крупные картинки на сцене; ответить можно голосом или нажатием
(для неговорящих детей). Нажатие не на ту картинку — не ошибка: герой мягко поправляет и
спрашивает снова, не тратя переспрос;public/images/lesson-a/);Ещё два урока по той же схеме — «О әрпі» (?lesson=letter-o: звук «о», слово «от», выбор от/мысық,
любимая игрушка) и «Ұ әрпі» (?lesson=letter-u: звук «у» по-казахски, слово «ұшақ», выбор
ұшақ/алма, «что тебе нравится?»). Реплики, принимаемые ответы и почему «Ұ», а не «У» —
docs/lesson-letters-o-u.md. Все буквенные уроки собраны в
LETTER_LESSONS (public/story.js); общий тест — tests/letter-lessons.test.js.
Мягкий режим (🕊 в панели оператора, ?mode=gentle, уроки включают его сами): тишина до
конца фразы 3,5 с вместо 1,7, ожидание ответа 15 с вместо 8, два переспроса вместо одного,
без искр, тряски и дрейфа фона (body.gentle). Пороги VAD_* в public/app.js остаются
полевыми значениями по умолчанию; профиль лежит рядом в GENTLE_VAD. Выбор оператора
сохраняется на устройстве (ertegim.gentle).
Кабинет логопеда / родителя — /cabinet.html (ссылка внизу главной и в панели оператора),
за PIN-экраном. Несколько детей на одном устройстве (public/profiles.js): у каждого своя история,
память повторов и наклейки (ключи localStorage получают суффикс :<id>; первый профиль остаётся на
старых ключах, так что существующие данные не теряются). На ребёнка задаются: мягкий режим
(всегда/никогда/как у занятия), число переспросов, пауза в конце фразы и ожидание ответа, «задание
на сегодня» (выделяется на главной) и сохранение транскриптов попыток — по умолчанию выключено,
как и обещает приватная заметка в отчёте. В кабинете: динамика (доля ответов с первого раза и время
реакции по занятиям), список занятий с попытками, наклейки, печать/PDF и экспорт JSON.
Наклейки (public/stickers.js): за пройденную сказку — «Түлкі досы», за героя на выбранном
пути — сова или медведь, за урок — его буква, за все ответы с первого раза — «Жұлдыз». Новые
показываются на финальном экране, вся коллекция (полученные и ещё нет, с подсказкой как получить) —
на главном. Хранятся в localStorage (ertegim.stickers), повторно не выдаются.
Реплики урока входят в tools/dump-story.js, поэтому tools/prerender.py озвучит их в
public/audio/. Пока .wav нет, текст реплики держится на экране примерно столько, сколько
её говорил бы герой (readingTimeMs), а не пролетает за полсекунды.
Данные живут в браузере одного устройства, поэтому кабинет логопеда и телефон родителей связаны
без сервера и аккаунтов (public/homework.js):
/#hw=…), он не уходит на сервер. Ссылку отправляют в WhatsApp/Telegram.История хранит до 100 занятий на ребёнка.
public/offline.js, public/sw.js). Service worker держит страницы и скрипты
(сначала сеть, потом кэш) и всю озвучку, картинки и ролики лисёнка (сначала кэш; для Safari
отдаёт куски файла по Range). /api/* не кэшируется никогда. Список файлов с хэшами —
public/offline-manifest.json, его пишет node tools/offline-manifest.js; тест падает, если
список устарел, поэтому после новой озвучки или картинок перезапустите генератор. В кабинете,
раздел «📱 Это устройство», — «Скачать для работы без интернета» с прогрессом.app.js, adultJudgeReason). Без сети, без микрофона или после двух
сбоев распознавания подряд лисёнок всё равно спрашивает, а взрослый жмёт «✅ Айтты» или
«🔁 Тағы бір рет» на большой полосе под репликой (на развилке — «Өзен» / «Орман»). Картинки
по-прежнему можно нажимать. В настройках ребёнка можно выбрать «Кто оценивает ответ: всегда
взрослый» — для тяжёлых нарушений речи, микрофон тогда не включается. Такие ответы идут в отчёт
отдельной строкой и не считаются «сказанными словами» или временем реакции.public/backup.js): все дети, история, задания, наклейки — один
файл и восстановление из него. Через 7 дней без копии кабинет напоминает баннером. Кабинет просит
браузер не очищать данные (navigator.storage.persist) и подсказывает на iPhone добавить сайт на
экран «Домой» — иначе Safari стирает данные через 7 дней без посещений./help.html — как заниматься дома и как логопеду вести пилот (каз./рус.,
печатается), /consent.html — бланк согласия родителей на один лист A4. Бланк — шаблон: перед
пилотом его нужно согласовать с юристом организации; казахский текст стоит вычитать носителю.public/errlog.js): последние 30 ошибок устройства (необработанные исключения,
сбои распознавания, микрофона, озвучки) — кнопка «Скопировать журнал» в кабинете. Ответов детей там
нет. Рядом версия сборки из /api/version (коммит на Vercel, git rev-parse локально).