ESP32-S3 Bench Guide
ESP32‑S3 N16R8 벤치 가이드: 실시간 핀아웃, 강의, 시뮬레이터 및 벤치 로그
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Интерактивная голосовая сказка: ребёнок 3–7 лет отвечает голосом и влияет на сюжет, реплики на казахском с русским подстрочником. Подробнее об идее — IDEA.md. Открыть http://localhost:3000 — это библиотека (выбор сказки или урока); сама сказка — http://localhost:3000/story.html. Панель оператора (скрыта от ребёнка) — клавиша `, значок ⚙ в шапке, либо параметр ?op=1 в URL (/story.html?op=1).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 локально).