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 |