как захватить, и передать звук на esp32 ? и воcпроизвести модулем max98357, начал еще с читалкой ковыряться, и в принципе все что надо на пк уже есть… русский еще не устанавливал, на английском озвучивает, (дуцк го и гогле по крайней мере, без интернета) не выспался сегодня еще, что то совсем туплю, и не знаю как захватить и отослать…
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Голос + ESP32</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 500px;
margin: 30px auto;
padding: 20px;
background: #f0f0f0;
}
.card {
background: white;
padding: 20px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
button {
padding: 10px 20px;
margin: 5px;
border: none;
border-radius: 8px;
font-size: 14px;
cursor: pointer;
font-weight: bold;
}
.btn-green { background: #28a745; color: white; }
.btn-green:hover { background: #218838; }
.btn-blue { background: #1a73e8; color: white; }
.btn-blue:hover { background: #1557b0; }
.btn-red { background: #dc3545; color: white; }
.btn-red:hover { background: #c82333; }
.btn-gray { background: #6c757d; color: white; }
.btn-gray:hover { background: #5a6268; }
textarea {
width: 100%;
height: 80px;
padding: 10px;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 14px;
font-family: inherit;
margin: 10px 0;
}
.status {
padding: 10px;
border-radius: 8px;
margin: 10px 0;
font-weight: bold;
}
.status.offline { background: #f8d7da; color: #721c24; }
.status.online { background: #d4edda; color: #155724; }
.log {
background: #1a1a1a;
color: #00ff00;
padding: 10px;
border-radius: 8px;
font-size: 12px;
font-family: monospace;
max-height: 150px;
overflow-y: auto;
margin-top: 10px;
white-space: pre-wrap;
}
</style>
</head>
<body>
<div class="card">
<h2>🔊 Голос + ESP32</h2>
<div class="status offline" id="status">❌ ESP32 не подключен</div>
<button class="btn-blue" id="connectBtn" onclick="connectESP32()">🔌 Подключить ESP32</button>
<button class="btn-gray" onclick="sendCommand('HELLO')">👋 Проверить</button>
<button class="btn-gray" onclick="sendCommand('BLINK')">💡 Мигнуть</button>
<textarea id="textInput" placeholder="Введите текст для озвучивания..."></textarea>
<button class="btn-green" onclick="speakText()">🔊 Озвучить текст</button>
<button class="btn-red" onclick="stopSpeaking()">⏹️ Стоп</button>
<div class="log" id="log">📡 Готов к работе...</div>
</div>
<script>
// ============================================================
// ПЕРЕМЕННЫЕ
// ============================================================
let port = null;
let isConnected = false;
let isSpeaking = false;
let currentUtterance = null;
// ============================================================
// ЛОГ
// ============================================================
function log(msg) {
const el = document.getElementById('log');
const time = new Date().toLocaleTimeString();
el.textContent = `[${time}] ${msg}\n` + el.textContent;
console.log(msg);
}
// ============================================================
// СТАТУС
// ============================================================
function setStatus(text, type) {
const el = document.getElementById('status');
el.textContent = text;
el.className = 'status ' + type;
document.getElementById('connectBtn').textContent =
type === 'online' ? '🔌 Отключить' : '🔌 Подключить ESP32';
}
// ============================================================
// ПОДКЛЮЧЕНИЕ К ESP32
// ============================================================
async function connectESP32() {
if (isConnected) {
await disconnect();
return;
}
try {
log('Запрос порта...');
port = await navigator.serial.requestPort();
log('Открытие порта...');
await port.open({ baudRate: 115200 });
isConnected = true;
setStatus('✅ ESP32 подключен', 'online');
log('✅ Подключено!');
// Запускаем чтение
readLoop();
} catch (err) {
log('❌ Ошибка: ' + err.message);
setStatus('❌ ' + err.message, 'offline');
isConnected = false;
}
}
async function disconnect() {
try {
if (port) {
await port.close();
port = null;
}
isConnected = false;
setStatus('❌ ESP32 отключен', 'offline');
log('🔌 Отключено');
} catch (err) {
log('Ошибка: ' + err.message);
}
}
// ============================================================
// ЧТЕНИЕ ОТВЕТОВ ОТ ESP32
// ============================================================
async function readLoop() {
if (!port) return;
try {
const decoder = new TextDecoderStream();
const readable = port.readable.pipeThrough(decoder);
const reader = readable.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
const lines = value.split('\n');
for (let line of lines) {
line = line.trim();
if (line) {
log('ESP32: ' + line);
}
}
}
} catch (err) {
if (isConnected) {
log('Ошибка чтения: ' + err.message);
}
}
}
// ============================================================
// ОТПРАВКА КОМАНД В ESP32
// ============================================================
async function sendCommand(cmd) {
if (!isConnected || !port) {
log('⚠️ ESP32 не подключен');
return;
}
try {
const writer = port.writable.getWriter();
const encoder = new TextEncoder();
await writer.write(encoder.encode(cmd + '\n'));
await writer.close();
log('📤 Отправлено: ' + cmd);
} catch (err) {
log('❌ Ошибка: ' + err.message);
}
}
// ============================================================
// ОЗВУЧИВАНИЕ ТЕКСТА (ГОЛОС)
// ============================================================
function speakText() {
const text = document.getElementById('textInput').value.trim();
if (!text) {
log('⚠️ Введите текст');
return;
}
// Отправляем текст в ESP32 (если подключен)
if (isConnected) {
sendCommand('TEXT:' + text);
}
// Озвучиваем через браузер (голос)
if (!window.speechSynthesis) {
log('❌ Speech Synthesis не поддерживается');
return;
}
stopSpeaking();
currentUtterance = new SpeechSynthesisUtterance(text);
currentUtterance.lang = 'ru-RU';
currentUtterance.rate = 1.0;
currentUtterance.pitch = 1.0;
// Ищем русский голос
const voices = speechSynthesis.getVoices();
const ruVoice = voices.find(v => v.lang.startsWith('ru'));
if (ruVoice) {
currentUtterance.voice = ruVoice;
}
currentUtterance.onstart = () => {
isSpeaking = true;
log('🔊 Говорит: ' + text);
};
currentUtterance.onend = () => {
isSpeaking = false;
log('✅ Готово');
};
currentUtterance.onerror = (e) => {
isSpeaking = false;
log('❌ Ошибка: ' + e.error);
};
speechSynthesis.speak(currentUtterance);
}
function stopSpeaking() {
if (window.speechSynthesis) {
window.speechSynthesis.cancel();
}
isSpeaking = false;
currentUtterance = null;
log('⏹️ Остановлено');
}
// ============================================================
// КЛАВИАТУРА
// ============================================================
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.key === 'Enter') {
e.preventDefault();
speakText();
}
});
// ============================================================
// ЗАГРУЗКА ГОЛОСОВ
// ============================================================
if (window.speechSynthesis) {
speechSynthesis.onvoiceschanged = () => {
log('🎤 Голоса загружены');
};
}
// ============================================================
// ЗАПУСК
// ============================================================
log('Приложение загружено');
setStatus('❌ ESP32 не подключен', 'offline');
if (!('serial' in navigator)) {
log('❌ Web Serial не поддерживается (используйте Chrome)');
document.getElementById('connectBtn').disabled = true;
} else {
log('✅ Web Serial доступен');
}
</script>
</body>
</html>
зайду если что завтра, ушел спать)))