Módulo de Cámara de Raspberry Pi

Esta página explicaba cómo emitir el vídeo de la cámara de la Raspberry al navegador con la biblioteca picamera. Esa biblioteca ya no funciona en Raspberry Pi OS actual: la sustituyó Picamera2. El montaje es el mismo; cambia el código que habla con la cámara.

Cómo se ha comprobado esta página. No tenemos el montaje físico en el banco de pruebas, así que la parte que habla con la cámara usa la API de Picamera2 tal como está en su código fuente actual, pero no se ha ejecutado con una cámara real. El servidor que emite el vídeo sí se probó entero, alimentándolo con fotogramas JPEG generados. Las salidas que aparecen son de esas ejecuciones.

Qué ha cambiado

Desde Raspberry Pi OS Bookworm, la cámara funciona con libcamera, y las herramientas antiguas —raspistill, raspivid y la biblioteca picamera— están, en palabras de la documentación oficial, «obsoletas desde hace muchos años y ahora sin soporte». Además, solo funcionaban con los primeros modelos de cámara.

AntesAhora
import picamerafrom picamera2 import Picamera2
raspistill, raspividrpicam-still, rpicam-vid
Activar la cámara en raspi-configse detecta sola al arrancar

Conectar y comprobar

El módulo de cámara conectado a la Raspberry Pi con su cable plano
El cable plano va al conector CSI, con los contactos del lado correcto. Si la cámara no aparece, casi siempre es esto.
rpicam-hello --list-cameras

Si la lista sale vacía, apaga, revisa la orientación del cable y vuelve a probar. Picamera2 viene ya instalada en Raspberry Pi OS; si no estuviera, se instala con sudo apt install python3-picamera2, con apt y no con pip, porque depende de libcamera, que es parte del sistema.

El servidor de vídeo

La idea es la misma que antes: la cámara entrega fotogramas JPEG, un objeto guarda el último, y un servidor web se lo va enviando al navegador uno tras otro con el tipo multipart/x-mixed-replace, que hace que la imagen se vaya sustituyendo. Esto es el servidor:

import io, threading, time, urllib.request
from http import server
from threading import Condition
from PIL import Image

PAGINA = """<!doctype html><meta charset="utf-8"><title>Cámara</title>
<h1>Raspberry Pi - Cámara</h1><img src="stream.mjpg" width="640" height="480">""".encode("utf-8")

class SalidaStreaming(io.BufferedIOBase):
    def __init__(self):
        self.frame = None
        self.condition = Condition()

    def write(self, buf):
        with self.condition:
            self.frame = buf
            self.condition.notify_all()

class Manejador(server.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == "/":
            self.send_response(200)
            self.send_header("Content-Type", "text/html; charset=utf-8")
            self.send_header("Content-Length", str(len(PAGINA)))
            self.end_headers()
            self.wfile.write(PAGINA)
        elif self.path == "/stream.mjpg":
            self.send_response(200)
            self.send_header("Cache-Control", "no-cache, private")
            self.send_header("Content-Type", "multipart/x-mixed-replace; boundary=FRAME")
            self.end_headers()
            try:
                while True:
                    with salida.condition:
                        salida.condition.wait()
                        frame = salida.frame
                    self.wfile.write(b"--FRAME\r\n")
                    self.send_header("Content-Type", "image/jpeg")
                    self.send_header("Content-Length", str(len(frame)))
                    self.end_headers()
                    self.wfile.write(frame)
                    self.wfile.write(b"\r\n")
            except (BrokenPipeError, ConnectionResetError):
                pass
        else:
            self.send_error(404)

    def log_message(self, *a):
        pass

class Servidor(server.ThreadingHTTPServer):
    allow_reuse_address = True
    daemon_threads = True

Y esto es lo que conecta la cámara con él:

from picamera2 import Picamera2
from picamera2.encoders import JpegEncoder
from picamera2.outputs import FileOutput

camara = Picamera2()
camara.configure(camara.create_video_configuration(main={"size": (640, 480)}))
salida = SalidaStreaming()
camara.start_recording(JpegEncoder(), FileOutput(salida))

try:
    Servidor(("", 8000), Manejador).serve_forever()
finally:
    camara.stop_recording()

Lo hemos probado sustituyendo la cámara por fotogramas generados:

salida/            -> 200 text/html; charset=utf-8
/stream.mjpg -> 200 multipart/x-mixed-replace; boundary=FRAME
fotogramas recibidos: 3 | cada uno empieza por b'\xff\xd8'

Cada fotograma empieza por FF D8, la firma de un JPEG. Abre http://IP-de-la-raspberry:8000 desde cualquier equipo de tu red; la IP la da hostname -I.

Un navegador mostrando el vídeo de la cámara de la Raspberry Pi en la dirección 192.168.0.17:8000
El resultado en el navegador, de la versión anterior de esta página.
Esto no tiene contraseña. Cualquiera en tu red puede ver la cámara. Para una red doméstica de confianza vale; si la vas a dejar accesible desde fuera, ponla detrás de algo con autenticación.

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.