Convertir Texto a Voz con gTTS en Python en una Raspberry

gTTS convierte texto en voz usando el servicio de Google Translate y guarda el resultado en MP3. En una Raspberry con un altavoz, eso es un aviso que habla. El ejemplo anterior tenía dos problemas: ya no arranca en Python 3.13 y su servidor no se podía abrir desde otro equipo.

Cómo se ha comprobado esta página. No tenemos el montaje físico en el banco de pruebas, así que el código se ejecutó en un contenedor con Python 3.14 y gTTS 2.5.4, generando los MP3 de verdad; lo único que no se probó es la reproducción por el altavoz. Las salidas que aparecen son de esas ejecuciones.

Instalar

En Raspberry Pi OS Bookworm y posteriores, pip install fuera de un entorno virtual da el error externally-managed-environment. Es a propósito: protege el Python del sistema. Hay dos caminos, que son los que recomienda la documentación oficial: instalar con apt el paquete python3-… si existe, o crear un entorno virtual e instalar ahí con pip. La versión anterior de esta página usaba sudo pip3 install, que en las versiones actuales ya no funciona y en las antiguas podía estropear el sistema.
python3 -m venv ~/voz
~/voz/bin/pip install gTTS
sudo apt install vlc

Texto a voz

from gtts import gTTS
import subprocess

gTTS(text="Saludos, terrícola", lang="es").save("voz.mp3")
subprocess.run(["cvlc", "--play-and-exit", "voz.mp3"])
salidavoz.mp3: 16512 bytes | empieza por: b'\\xff\\xf3\\x84'

Un MP3 de 16 KB. cvlc es VLC sin ventana; --play-and-exit hace que termine al acabar. subprocess.run con la orden en una lista es más seguro que el os.system de antes: si algún día el nombre del archivo viene de fuera, no se puede colar otra orden en él.

gTTS necesita internet. No genera la voz en la Raspberry: se la pide a Google. Sin conexión no funciona, y tiene un límite de uso, así que no sirve para generar miles de audios. Para voz sin conexión, en Linux está espeak-ng, con un sonido mucho más robótico.
Para…Usa
Español de Españalang="es"
Otro acentoel parámetro tld, por ejemplo tld="com.mx"
Más despacioslow=True
Ver los idiomasgtts.lang.tts_langs(): son 69

Mandar mensajes desde el móvil

from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import parse_qs
from io import BytesIO
from gtts import gTTS
import subprocess, tempfile

PAGINA = """<!doctype html><meta charset="utf-8"><title>Manda un saludo</title>
<form method="post"><textarea name="texto" rows="4" cols="50"></textarea>
<button>Enviar</button></form>""".encode("utf-8")

class Manejador(BaseHTTPRequestHandler):
    def do_GET(self):
        self.responder(200, "text/html; charset=utf-8", PAGINA)

    def do_POST(self):
        largo = int(self.headers.get("Content-Length", 0))
        datos = parse_qs(self.rfile.read(largo).decode("utf-8"))
        texto = datos.get("texto", [""])[0].strip()[:300]
        if not texto:
            self.responder(400, "text/plain; charset=utf-8", "Escribe algo".encode())
            return
        with tempfile.NamedTemporaryFile(suffix=".mp3") as f:
            gTTS(text=texto, lang="es").write_to_fp(f)
            f.flush()
            subprocess.run(["cvlc", "--play-and-exit", f.name])
        self.responder(200, "text/html; charset=utf-8", PAGINA)

    def responder(self, codigo, tipo, cuerpo):
        self.send_response(codigo)
        self.send_header("Content-Type", tipo)
        self.send_header("Content-Length", str(len(cuerpo)))
        self.end_headers()
        self.wfile.write(cuerpo)

ThreadingHTTPServer(("0.0.0.0", 8080), Manejador).serve_forever()

Se probó la misma lógica devolviendo el audio en lugar de reproducirlo:

salidaGET  -> 200 text/html; charset=utf-8
POST -> 200 audio/mpeg 14016 bytes de audio
POST vacío -> 400

Los dos fallos del ejemplo anterior

1. Usaba el módulo cgi, que ya no existe. Se eliminó en Python 3.13:

salidaModuleNotFoundError: No module named 'cgi'

Para leer un formulario basta urllib.parse.parse_qs, como en el código de arriba.

2. Escuchaba en "localhost". Así el servidor solo acepta conexiones de la propia Raspberry, y desde el móvil la página no carga. Con "0.0.0.0" escucha en todas sus direcciones; la IP para abrirla desde otro equipo la da hostname -I. Y como cualquiera de tu red podrá hacer hablar a la Raspberry, el texto se corta a 300 caracteres.

La página para enviar un saludo abierta en el navegador
La página del servidor, de la versión anterior de este artículo.

Revisada en octubre de 2026 para Raspberry Pi OS actual. Las salidas están copiadas de ejecuciones reales; lo que no se pudo ejecutar sin el hardware se indica en el propio texto.