Compare commits
No commits in common. "b68896754bac5ecf7d1e4c1dc4390665a21935e1" and "6bc9a5cb4438f982696af5ea7a2488e3af109729" have entirely different histories.
b68896754b
...
6bc9a5cb44
BIN
ElevenLabs.mp3
Normal file
BIN
ElevenLabs1.mp3
Normal file
BIN
Rodrigo Cahuantzi_1.jpg
Normal file
|
After Width: | Height: | Size: 18 KiB |
350
audio_iot.py
@ -1,350 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
from pydub import AudioSegment
|
|
||||||
import os
|
|
||||||
import threading
|
|
||||||
import http.server
|
|
||||||
import socketserver
|
|
||||||
import socket
|
|
||||||
import time
|
|
||||||
import subprocess
|
|
||||||
import urllib.request # Para enviar HTTP GET a la ESP32 (sin librerías extra)
|
|
||||||
|
|
||||||
|
|
||||||
# ================= CONFIGURACIÓN =================
|
|
||||||
IP_BOCINA = "192.168.15.135"
|
|
||||||
PUERTO_ESP32 = 80
|
|
||||||
PUERTO_HTTP = 8000
|
|
||||||
CARPETA_AUDIO = "public_audio"
|
|
||||||
|
|
||||||
if not os.path.exists(CARPETA_AUDIO):
|
|
||||||
os.makedirs(CARPETA_AUDIO)
|
|
||||||
|
|
||||||
# ================= AUTO-DETECTAR MI IP =================
|
|
||||||
def obtener_mi_ip():
|
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
try:
|
|
||||||
s.connect((IP_BOCINA, 80))
|
|
||||||
IP = s.getsockname()[0]
|
|
||||||
except Exception:
|
|
||||||
IP = '127.0.0.1'
|
|
||||||
finally:
|
|
||||||
s.close()
|
|
||||||
return IP
|
|
||||||
|
|
||||||
MI_IP = obtener_mi_ip()
|
|
||||||
print(f"[RED] IP auto-detectada para el servidor de audio: {MI_IP}")
|
|
||||||
|
|
||||||
|
|
||||||
def iniciar_servidor_http():
|
|
||||||
class Handler(http.server.SimpleHTTPRequestHandler):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, directory=CARPETA_AUDIO, **kwargs)
|
|
||||||
|
|
||||||
def log_message(self, format, *args):
|
|
||||||
print(f"[SERVIDOR HTTP] Petición de ESP32: {args[0]}")
|
|
||||||
|
|
||||||
def handle(self):
|
|
||||||
try:
|
|
||||||
super().handle()
|
|
||||||
except (BrokenPipeError, ConnectionResetError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def copyfile(self, source, outputfile):
|
|
||||||
try:
|
|
||||||
super().copyfile(source, outputfile)
|
|
||||||
except (BrokenPipeError, ConnectionResetError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class ReusableTCPServer(socketserver.TCPServer):
|
|
||||||
allow_reuse_address = True
|
|
||||||
|
|
||||||
try:
|
|
||||||
with ReusableTCPServer(("", PUERTO_HTTP), Handler) as httpd:
|
|
||||||
print(f"Servidor de Audio activo en http://{MI_IP}:{PUERTO_HTTP}")
|
|
||||||
httpd.serve_forever()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"ERROR CRÍTICO AL LEVANTAR EL SERVIDOR: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
# ================= ORDEN A LA ESP32 =================
|
|
||||||
# La ESP32 usa HTTP GET en /play?url=..., NO WebSocket.
|
|
||||||
# Antes usábamos WebSocket al puerto 81 → la ESP32 nunca recibió nada.
|
|
||||||
def _mandar_orden_reproduccion(nombre_archivo):
|
|
||||||
url_mp3 = f"http://{MI_IP}:{PUERTO_HTTP}/{nombre_archivo}"
|
|
||||||
# Construimos la URL exacta que espera el handlePlay() de la ESP32
|
|
||||||
url_orden = f"http://{IP_BOCINA}:{PUERTO_ESP32}/play?url={url_mp3}"
|
|
||||||
|
|
||||||
try:
|
|
||||||
print(f"Ordenando a ESP32 reproducir: {url_mp3}")
|
|
||||||
with urllib.request.urlopen(url_orden, timeout=5) as respuesta:
|
|
||||||
estado = respuesta.read().decode()
|
|
||||||
print(f"ESP32 respondió: {estado}")
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
print(f" Error enviando orden a la bocina: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# ================= CONTROL DE VOLUMEN =================
|
|
||||||
|
|
||||||
def _mandar_orden_volumen(nivel=18):
|
|
||||||
"""Establece volumen directamente (0-21)"""
|
|
||||||
url_orden = f"http://{IP_BOCINA}:{PUERTO_ESP32}/volume?level={nivel}"
|
|
||||||
try:
|
|
||||||
with urllib.request.urlopen(url_orden, timeout=5) as respuesta:
|
|
||||||
print(f"Volumen: {respuesta.read().decode()}")
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error cambiando volumen: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def subir_volumen():
|
|
||||||
"""Sube 1 nivel de volumen"""
|
|
||||||
url_orden = f"http://{IP_BOCINA}:{PUERTO_ESP32}/volume_up"
|
|
||||||
try:
|
|
||||||
with urllib.request.urlopen(url_orden, timeout=5) as respuesta:
|
|
||||||
print(f" {respuesta.read().decode()}")
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def bajar_volumen():
|
|
||||||
"""Baja 1 nivel de volumen"""
|
|
||||||
url_orden = f"http://{IP_BOCINA}:{PUERTO_ESP32}/volume_down"
|
|
||||||
try:
|
|
||||||
with urllib.request.urlopen(url_orden, timeout=5) as respuesta:
|
|
||||||
print(f"{respuesta.read().decode()}")
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
# ================= LÓGICA PRINCIPAL =================
|
|
||||||
def enviar_archivos_bocina(lista_archivos):
|
|
||||||
if not lista_archivos:
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 1. Unimos los audios
|
|
||||||
audio_final = AudioSegment.empty()
|
|
||||||
for archivo in lista_archivos:
|
|
||||||
if os.path.exists(archivo):
|
|
||||||
audio_final += AudioSegment.from_file(archivo)
|
|
||||||
|
|
||||||
# 2. WAV temporal
|
|
||||||
ruta_temp = os.path.join(CARPETA_AUDIO, "temp.wav")
|
|
||||||
audio_final.export(ruta_temp, format="wav")
|
|
||||||
|
|
||||||
# 3. Nombre único anti-caché
|
|
||||||
timestamp = int(time.time())
|
|
||||||
nombre_salida = f"saludo_{timestamp}.mp3"
|
|
||||||
ruta_salida = os.path.join(CARPETA_AUDIO, nombre_salida)
|
|
||||||
|
|
||||||
# 4. MP3 ligero para reducir la presión sobre el buffer de Audio.h
|
|
||||||
# El fix completo del OOM va en el .ino (ver abajo)
|
|
||||||
comando_ffmpeg = [
|
|
||||||
"/usr/bin/ffmpeg", "-y",
|
|
||||||
"-i", ruta_temp,
|
|
||||||
"-codec:a", "libmp3lame",
|
|
||||||
"-b:a", "48k",
|
|
||||||
"-ar", "24000",
|
|
||||||
"-ac", "1",
|
|
||||||
# FILTRO ACÚSTICO: Corta todo lo que no sea voz humana
|
|
||||||
"-af", "highpass=f=150, lowpass=f=7500",
|
|
||||||
"-map_metadata", "-1",
|
|
||||||
"-id3v2_version", "0",
|
|
||||||
"-write_xing", "0",
|
|
||||||
ruta_salida
|
|
||||||
]
|
|
||||||
subprocess.run(comando_ffmpeg, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
||||||
|
|
||||||
# 5. Limpieza del temporal
|
|
||||||
if os.path.exists(ruta_temp):
|
|
||||||
os.remove(ruta_temp)
|
|
||||||
|
|
||||||
# 6. Enviamos la orden en un hilo separado
|
|
||||||
# (evita conflicto con el runtime de InsightFace/ONNX)
|
|
||||||
def _lanzar_orden():
|
|
||||||
time.sleep(0.5) # Aseguramos que el archivo ya está en disco
|
|
||||||
_mandar_orden_reproduccion(nombre_salida)
|
|
||||||
|
|
||||||
# Limpiamos MP3 viejos DESPUÉS de que la ESP32 confirmó
|
|
||||||
for archivo in os.listdir(CARPETA_AUDIO):
|
|
||||||
if archivo.startswith("saludo_") and archivo != nombre_salida:
|
|
||||||
try:
|
|
||||||
os.remove(os.path.join(CARPETA_AUDIO, archivo))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
hilo = threading.Thread(target=_lanzar_orden, daemon=True)
|
|
||||||
hilo.start()
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error procesando el audio: {e}")
|
|
||||||
|
|
||||||
# ==============================================================================
|
|
||||||
# AUTO-ARRANQUE DEL SERVIDOR (Hilo en segundo plano)
|
|
||||||
# ==============================================================================
|
|
||||||
servidor_thread = threading.Thread(target=iniciar_servidor_http, daemon=True)
|
|
||||||
servidor_thread.start()
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
import asyncio
|
|
||||||
from pydub import AudioSegment
|
|
||||||
import os
|
|
||||||
import threading
|
|
||||||
import http.server
|
|
||||||
import socketserver
|
|
||||||
import socket
|
|
||||||
import time
|
|
||||||
import subprocess
|
|
||||||
import urllib.request # Para enviar HTTP GET a la ESP32 (sin librerías extra)
|
|
||||||
|
|
||||||
|
|
||||||
# ================= CONFIGURACIÓN =================
|
|
||||||
IP_BOCINA = "192.168.1.77" # La IP de la ESP32 que se definio como estatica
|
|
||||||
PUERTO_ESP32 = 80 # La ESP32 escucha HTTP en el puerto 80
|
|
||||||
PUERTO_HTTP = 8000
|
|
||||||
CARPETA_AUDIO = "public_audio"
|
|
||||||
|
|
||||||
if not os.path.exists(CARPETA_AUDIO):
|
|
||||||
os.makedirs(CARPETA_AUDIO)
|
|
||||||
|
|
||||||
|
|
||||||
# ================= SERVIDOR HTTP =================
|
|
||||||
def obtener_mi_ip():
|
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
try:
|
|
||||||
s.connect(('10.255.255.255', 1))
|
|
||||||
IP = s.getsockname()[0]
|
|
||||||
except Exception:
|
|
||||||
IP = '127.0.0.1'
|
|
||||||
finally:
|
|
||||||
s.close()
|
|
||||||
return IP
|
|
||||||
|
|
||||||
|
|
||||||
MI_IP = obtener_mi_ip()
|
|
||||||
|
|
||||||
|
|
||||||
def iniciar_servidor_http():
|
|
||||||
class Handler(http.server.SimpleHTTPRequestHandler):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, directory=CARPETA_AUDIO, **kwargs)
|
|
||||||
|
|
||||||
def log_message(self, format, *args):
|
|
||||||
print(f"[SERVIDOR HTTP] Petición de ESP32: {args[0]}")
|
|
||||||
|
|
||||||
def handle(self):
|
|
||||||
try:
|
|
||||||
super().handle()
|
|
||||||
except (BrokenPipeError, ConnectionResetError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def copyfile(self, source, outputfile):
|
|
||||||
try:
|
|
||||||
super().copyfile(source, outputfile)
|
|
||||||
except (BrokenPipeError, ConnectionResetError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class ReusableTCPServer(socketserver.TCPServer):
|
|
||||||
allow_reuse_address = True
|
|
||||||
|
|
||||||
try:
|
|
||||||
with ReusableTCPServer(("", PUERTO_HTTP), Handler) as httpd:
|
|
||||||
print(f"Servidor de Audio activo en http://{MI_IP}:{PUERTO_HTTP}")
|
|
||||||
httpd.serve_forever()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"ERROR CRÍTICO AL LEVANTAR EL SERVIDOR: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
# ================= ORDEN A LA ESP32 =================
|
|
||||||
# La ESP32 usa HTTP GET en /play?url=..., NO WebSocket.
|
|
||||||
# Antes usábamos WebSocket al puerto 81 → la ESP32 nunca recibió nada.
|
|
||||||
def _mandar_orden_reproduccion(nombre_archivo):
|
|
||||||
url_mp3 = f"http://{MI_IP}:{PUERTO_HTTP}/{nombre_archivo}"
|
|
||||||
# Construimos la URL exacta que espera el handlePlay() de la ESP32
|
|
||||||
url_orden = f"http://{IP_BOCINA}:{PUERTO_ESP32}/play?url={url_mp3}"
|
|
||||||
|
|
||||||
try:
|
|
||||||
print(f"Ordenando a ESP32 reproducir: {url_mp3}")
|
|
||||||
with urllib.request.urlopen(url_orden, timeout=5) as respuesta:
|
|
||||||
estado = respuesta.read().decode()
|
|
||||||
print(f"ESP32 respondió: {estado}")
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
print(f" Error enviando orden a la bocina: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
# ================= LÓGICA PRINCIPAL =================
|
|
||||||
def enviar_archivos_bocina(lista_archivos):
|
|
||||||
if not lista_archivos:
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 1. Unimos los audios
|
|
||||||
audio_final = AudioSegment.empty()
|
|
||||||
for archivo in lista_archivos:
|
|
||||||
if os.path.exists(archivo):
|
|
||||||
audio_final += AudioSegment.from_file(archivo)
|
|
||||||
|
|
||||||
# 2. WAV temporal
|
|
||||||
ruta_temp = os.path.join(CARPETA_AUDIO, "temp.wav")
|
|
||||||
audio_final.export(ruta_temp, format="wav")
|
|
||||||
|
|
||||||
# 3. Nombre único anti-caché
|
|
||||||
timestamp = int(time.time())
|
|
||||||
nombre_salida = f"saludo_{timestamp}.mp3"
|
|
||||||
ruta_salida = os.path.join(CARPETA_AUDIO, nombre_salida)
|
|
||||||
|
|
||||||
# 4. MP3 ligero para reducir la presión sobre el buffer de Audio.h
|
|
||||||
# El fix completo del OOM va en el .ino (ver abajo)
|
|
||||||
comando_ffmpeg = [
|
|
||||||
"/usr/bin/ffmpeg", "-y",
|
|
||||||
"-i", ruta_temp,
|
|
||||||
"-codec:a", "libmp3lame",
|
|
||||||
"-b:a", "48k",
|
|
||||||
"-ar", "24000",
|
|
||||||
"-ac", "1",
|
|
||||||
# FILTRO ACÚSTICO: Corta todo lo que no sea voz humana
|
|
||||||
"-af", "highpass=f=150, lowpass=f=7500",
|
|
||||||
"-map_metadata", "-1",
|
|
||||||
"-id3v2_version", "0",
|
|
||||||
"-write_xing", "0",
|
|
||||||
ruta_salida
|
|
||||||
]
|
|
||||||
subprocess.run(comando_ffmpeg, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
||||||
|
|
||||||
# 5. Limpieza del temporal
|
|
||||||
if os.path.exists(ruta_temp):
|
|
||||||
os.remove(ruta_temp)
|
|
||||||
|
|
||||||
# 6. Enviamos la orden en un hilo separado
|
|
||||||
# (evita conflicto con el runtime de InsightFace/ONNX)
|
|
||||||
def _lanzar_orden():
|
|
||||||
time.sleep(0.5) # Aseguramos que el archivo ya está en disco
|
|
||||||
_mandar_orden_reproduccion(nombre_salida)
|
|
||||||
|
|
||||||
# Limpiamos MP3 viejos DESPUÉS de que la ESP32 confirmó
|
|
||||||
for archivo in os.listdir(CARPETA_AUDIO):
|
|
||||||
if archivo.startswith("saludo_") and archivo != nombre_salida:
|
|
||||||
try:
|
|
||||||
os.remove(os.path.join(CARPETA_AUDIO, archivo))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
hilo = threading.Thread(target=_lanzar_orden, daemon=True)
|
|
||||||
hilo.start()
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error procesando el audio: {e}")
|
|
||||||
|
|
||||||
# ==============================================================================
|
|
||||||
# AUTO-ARRANQUE DEL SERVIDOR (Hilo en segundo plano)
|
|
||||||
# ==============================================================================
|
|
||||||
servidor_thread = threading.Thread(target=iniciar_servidor_http, daemon=True)
|
|
||||||
servidor_thread.start()
|
|
||||||
"""
|
|
||||||
225
bocina_esp32.ino
@ -1,225 +0,0 @@
|
|||||||
#include <Arduino.h>
|
|
||||||
#include <WiFi.h>
|
|
||||||
#include <WebServer.h>
|
|
||||||
#include "Audio.h"
|
|
||||||
|
|
||||||
const char* ssid = "SmartEstancia";
|
|
||||||
const char* password = "Smart12345";
|
|
||||||
|
|
||||||
IPAddress local_IP(192, 168, 1, 77);
|
|
||||||
IPAddress gateway(192, 168, 1, 254);
|
|
||||||
IPAddress subnet(255, 255, 255, 0);
|
|
||||||
IPAddress primaryDNS(8, 8, 8, 8);
|
|
||||||
IPAddress secondaryDNS(8, 8, 4, 4);
|
|
||||||
|
|
||||||
#define I2S_BCLK 26
|
|
||||||
#define I2S_LRC 25
|
|
||||||
#define I2S_DOUT 33
|
|
||||||
|
|
||||||
Audio audio;
|
|
||||||
WebServer server(80);
|
|
||||||
|
|
||||||
void handlePlay() {
|
|
||||||
if (server.hasArg("url")) {
|
|
||||||
String url = server.arg("url");
|
|
||||||
Serial.print("🎵 Python ordenó reproducir: ");
|
|
||||||
Serial.println(url);
|
|
||||||
audio.stopSong();
|
|
||||||
audio.connecttohost(url.c_str());
|
|
||||||
server.send(200, "text/plain", "Reproduciendo OK");
|
|
||||||
} else {
|
|
||||||
server.send(400, "text/plain", "Error: Falta la URL");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- NUEVO: Control de Volumen ---
|
|
||||||
void handleVolume() {
|
|
||||||
if (server.hasArg("level")) {
|
|
||||||
int nivel = server.arg("level").toInt();
|
|
||||||
if (nivel >= 0 && nivel <= 21) {
|
|
||||||
audio.setVolume(nivel);
|
|
||||||
Serial.print("🔊 Volumen cambiado a: ");
|
|
||||||
Serial.println(nivel);
|
|
||||||
server.send(200, "text/plain", "Volumen OK: " + String(nivel));
|
|
||||||
} else {
|
|
||||||
server.send(400, "text/plain", "Error: Nivel debe ser 0-21");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
server.send(400, "text/plain", "Error: Falta parametro 'level'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleVolumeUp() {
|
|
||||||
int actual = audio.getVolume();
|
|
||||||
if (actual < 21) audio.setVolume(actual + 1);
|
|
||||||
server.send(200, "text/plain", "Volumen: " + String(audio.getVolume()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleVolumeDown() {
|
|
||||||
int actual = audio.getVolume();
|
|
||||||
if (actual > 0) audio.setVolume(actual - 1);
|
|
||||||
server.send(200, "text/plain", "Volumen: " + String(audio.getVolume()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
Serial.println("\n--- INICIANDO SISTEMA DE AUDIO ESP32 ---");
|
|
||||||
|
|
||||||
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
|
|
||||||
Serial.println("Fallo al configurar la IP Estatica");
|
|
||||||
}
|
|
||||||
|
|
||||||
WiFi.begin(ssid, password);
|
|
||||||
Serial.print("Conectando a WiFi");
|
|
||||||
|
|
||||||
int intento = 0;
|
|
||||||
while (WiFi.status() != WL_CONNECTED && intento < 20) {
|
|
||||||
delay(500);
|
|
||||||
Serial.print(".");
|
|
||||||
intento++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WiFi.status() != WL_CONNECTED) {
|
|
||||||
Serial.println("\nFallo de conexion.");
|
|
||||||
while(true){ delay(1000); }
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println("\nWiFi Conectado!");
|
|
||||||
Serial.print("IP FIJA: ");
|
|
||||||
Serial.println(WiFi.localIP());
|
|
||||||
|
|
||||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
|
||||||
audio.setVolume(15); // Volumen inicial medio
|
|
||||||
|
|
||||||
server.on("/play", HTTP_GET, handlePlay);
|
|
||||||
server.on("/volume", HTTP_GET, handleVolume);
|
|
||||||
server.on("/volume_up", HTTP_GET, handleVolumeUp);
|
|
||||||
server.on("/volume_down", HTTP_GET, handleVolumeDown);
|
|
||||||
|
|
||||||
server.begin();
|
|
||||||
Serial.println("Listo para recibir audio...");
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
audio.loop();
|
|
||||||
server.handleClient();
|
|
||||||
|
|
||||||
if (Serial.available()) {
|
|
||||||
String url = Serial.readStringUntil('\n');
|
|
||||||
url.trim();
|
|
||||||
if (url.length() > 5 && url.startsWith("http")) {
|
|
||||||
audio.stopSong();
|
|
||||||
audio.connecttohost(url.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void audio_info(const char *info){ Serial.print("INFO AUDIO: "); Serial.println(info); }
|
|
||||||
void audio_id3data(const char *info){ Serial.print("METADATOS: "); Serial.println(info); }
|
|
||||||
void audio_eof_mp3(const char *info){ Serial.print("FIN DEL AUDIO: "); Serial.println(info); }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <WiFi.h>
|
|
||||||
#include <WebServer.h>
|
|
||||||
#include "Audio.h"
|
|
||||||
|
|
||||||
// =======================================================
|
|
||||||
// 1. CONFIGURACIÓN DE RED
|
|
||||||
// =======================================================
|
|
||||||
const char* ssid = "SmartEstancia";
|
|
||||||
const char* password = "Smart12345";
|
|
||||||
|
|
||||||
// --- CONFIGURACIÓN DE IP ESTÁTICA ---
|
|
||||||
IPAddress local_IP(192, 168, 1, 77); // La IP que le asiganamos a la bocina
|
|
||||||
IPAddress gateway(192, 168, 1, 254); // la IP del router
|
|
||||||
IPAddress subnet(255, 255, 255, 0); // Máscara de subred
|
|
||||||
IPAddress primaryDNS(8, 8, 8, 8); // DNS primario (Google)
|
|
||||||
IPAddress secondaryDNS(8, 8, 4, 4); // DNS secundario (Google)
|
|
||||||
|
|
||||||
// =======================================================
|
|
||||||
// 2. CONFIGURACIÓN DE PINES CORRECTOS
|
|
||||||
// =======================================================
|
|
||||||
#define I2S_BCLK 26
|
|
||||||
#define I2S_LRC 25
|
|
||||||
#define I2S_DOUT 33
|
|
||||||
|
|
||||||
Audio audio;
|
|
||||||
WebServer server(80);
|
|
||||||
|
|
||||||
void handlePlay() {
|
|
||||||
if (server.hasArg("url")) {
|
|
||||||
String url = server.arg("url");
|
|
||||||
Serial.print("🎵 Python ordenó reproducir: ");
|
|
||||||
Serial.println(url);
|
|
||||||
audio.stopSong();
|
|
||||||
audio.connecttohost(url.c_str());
|
|
||||||
server.send(200, "text/plain", "Reproduciendo OK");
|
|
||||||
} else {
|
|
||||||
server.send(400, "text/plain", "Error: Falta la URL");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Serial.begin(115200);
|
|
||||||
Serial.println("\n--- INICIANDO SISTEMA DE AUDIO ESP32 ---");
|
|
||||||
|
|
||||||
if (psramFound()) {
|
|
||||||
Serial.println("PSRAM encontrada.");
|
|
||||||
} else {
|
|
||||||
Serial.println(" Sin PSRAM. Usando RAM interna.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ⚡ APLICAR IP ESTÁTICA ANTES DE CONECTAR
|
|
||||||
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
|
|
||||||
Serial.println(" Fallo al configurar la IP Estática");
|
|
||||||
}
|
|
||||||
|
|
||||||
WiFi.begin(ssid, password);
|
|
||||||
Serial.print("Conectando a WiFi");
|
|
||||||
|
|
||||||
int intento = 0;
|
|
||||||
while (WiFi.status() != WL_CONNECTED && intento < 20) {
|
|
||||||
delay(500);
|
|
||||||
Serial.print(".");
|
|
||||||
intento++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WiFi.status() != WL_CONNECTED) {
|
|
||||||
Serial.println("\n Fallo de conexión.");
|
|
||||||
while(true){ delay(1000); }
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println("\n WiFi Conectado de verdad!");
|
|
||||||
Serial.print("Tu IP es FIJA: ");
|
|
||||||
Serial.println(WiFi.localIP());
|
|
||||||
|
|
||||||
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
|
|
||||||
audio.setVolume(18);
|
|
||||||
|
|
||||||
server.on("/play", HTTP_GET, handlePlay);
|
|
||||||
server.begin();
|
|
||||||
Serial.println("Listo para recibir audio...");
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
audio.loop();
|
|
||||||
server.handleClient();
|
|
||||||
|
|
||||||
if (Serial.available()) {
|
|
||||||
String url = Serial.readStringUntil('\n');
|
|
||||||
url.trim();
|
|
||||||
if (url.length() > 5 && url.startsWith("http")) {
|
|
||||||
audio.stopSong();
|
|
||||||
audio.connecttohost(url.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void audio_info(const char *info){ Serial.print("INFO AUDIO: "); Serial.println(info); }
|
|
||||||
void audio_id3data(const char *info){ Serial.print("METADATOS: "); Serial.println(info); }
|
|
||||||
void audio_eof_mp3(const char *info){ Serial.print("FIN DEL AUDIO: "); Serial.println(info); }
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |