raspberry-pi-iot Innovación

DIY: Cómo hacer un portero automático WiFi con una Raspberry Pi

30/07/19 16 min. de lectura

En este post te voy a explicar cómo he construido un portero automático para mi casa con una Raspberry Pi Zero W. La prueba de concepto que he realizado es bastante sencilla. A lo largo del post te cuento qué necesitas, cómo hacer el diseño de la arquitectura y cómo construir paso a paso un portero que en el mercado cuesta más de 100€.

👉 Aquí puedes ver una Review del último lanzamiento: Raspberry Pi 4

Antes de empezar:

Qué necesitas para hacer un portero automático con WiFi

PRODUCTOPRECIO
Raspberry Pi Zero WH + adaptador HDMI y USB26.48€
Tarjeta microSD 32 GB5.99€
Fuente de alimentación 5V2.99€
Amplificador de audio1.97€
Módulo 2 relés 5V1.89€
Adaptador audio6.99€
Jacks audio2.99€
Micrófono, altavoz, botonera, cables, cajasReutilizados
TOTAL49.30€

Construir un portero automático WiFi paso a paso:

Indice

Definición de necesidades y diseño

Prototipado y montaje

Configuración del Software

Código y configuración de cada bloque

Aspecto final

Lecciones aprendidas

1. Definición de necesidades y creación del diseño

Para cumplir con mis necesidades, el portero debía tener las siguientes características:

  1. Comunicarse por WiFi.
  2. Comunicarse por SIP estándar para establecer llamadas con cualquier tipo de terminal.
  3. Controlar la apertura de una puerta y una valla.
  4. Ofrecer una API sobre HTTPS para controlar la apertura desde una página web o aplicación sencilla.
  5. No necesitar de ningún dispositivo específico adicional.

Una vez definidas las necesidades es el turno de valorar las opciones disponibles. Básicamente me movía entre el mundo Arduino, ESP32 y Raspberry Pi. Como te puedes imaginar: ganó Raspberry Pi.

Preparé un esquema con los elementos fundamentales del sistema:

esquema portero automatico wifi con raspberry pi
Arquitectura para un portero automático WiFi con Raspberry Pi
  • Raspberry Pi Zero W: Me ofrece WiFi, varios pines programables con los que actuar sobre elementos externos y un sistema operativo Linux (Raspbian). Si quieres evitar hacer soldaduras puedes utilizar el modelo WH.
  • Web Server: Presenta las funciones accesibles mediante HTTPS para la apertura de puertas.
  • SIP PBX: Gestiona la telefonía: registro de terminales, gestión de colas, música de espera, etc.
  • Teléfonos:
    • El softphone que se ejecuta en la Raspberry hace la función de comunicación con la calle.
    • Los teléfonos de mi casa son terminales DECT Siemens Gigaset C470 IP, dependientes de una estación base con capacidades SIP.
    • En los móviles instalé la versión gratuita de Zoiper, un cliente SIP multiplataforma.
  • Para abrir la puerta y la valla utilicé un sistema de 2 relés alimentado a 5 voltios. Cualquiera sirve. En mi caso usé uno de Bobury.
  • Audio: Las Raspberry no tienen soporte de micrófono, así que he conectado un adaptador de sonido USB. El primero que probé me daba muchos problemas de ruido. Finalmente escogí uno de Sabrent.
  • Módulo de calle: Aproveché el módulo de calle de mi portero anterior. Incluye altavoz, micrófono y botonera.
  • Otros:
    • Caja estanca: Aproveché la caja estanca de mi antiguo portero.
    • Alimentación: La Raspberry se alimenta a 5 voltios. Un enchufe con un cargador de móvil no cabría en la caja, de manera que añadí una fuente de alimentación externa de KKMoon. Esto además me permitiría alimentar los relés y otros elementos sin pasar por la Raspberry. Así evito sobrecargarla.

2. El montaje

Hice una primera maqueta en una placa de prototipado para comprobar que todo funcionaba bien. El esquema muestra el prototipo y sus conexiones:

prototipo portero automatico wifi con raspberry
Prototipo portero automático WiFi con Raspberry Pi

El GPIO de la Raspberry Pi Zero W nos permite alimentar la tarjeta y controlar varios dispositivos. Aunque alimentar las Raspberrys directamente por GPIO no suele estar recomendado, ya que los pines no están protegidos contra sobrevoltajes, en los foros se da por hecho que las Zero no tienen esa protección en el puerto de alimentación USB, de manera que es indiferente alimentarla de una manera u otra.

Para este proyecto utilicé los siguientes pines:

FUNCIÓNID. WIRINGPIPIN
+5V2
0V6
BOTÓN (LECTURA)011
RELÉ PUERTA (ESCRITURA)112
RELÉ VALLA (ESCRITURA)213

3. Configuración del software

Todo el software que he utilizado ofrece librerías para C. No soy desarrollador y para una prueba de concepto sencilla he preferido manejarme con scripts de shell antes que dedicar el tiempo a recordar C. Así que uno de los elementos de decisión para elegir el software ha sido que me ofreciera aplicaciones listas para ejecutar que pudiera utilizar desde scripts.

Las necesidades eran:

  • Control de GPIO: Lo realicé con WiringPi. Ofrece la aplicación gpio para gestionar el GPIO desde shell.
  • Web Server: Me decidí por utilizar lighthttpd. Es ligero y sencillo.
  • SIP PBX: Hice varias pruebas con sistemas de administración sencilla, pero para la prueba de concepto decidí quedarme con Asterisk. Para una configuración elemental resulta más sencillo y ligero que las plataformas que añaden capas para simplificar la gestión de una centralita de uso general. Probé FreePBX, pero la Raspberry apenas podía con él.
  • Softphone: Utilicé la aplicación pjsua de pjsip.

4. Código y configuración de cada bloque

Apertura de la puerta y valla

Se realiza desde peticiones HTTPS. Para esta prueba rápida he hecho un par de cgi básicos a los que se llama desde una página de control.

Página de apertura de puerta y valla:

<html>

<head>
        <title>Portero automático 0.2 Beta</title>
        <link rel="shortcut icon" href="/door-icon.ico">
        <link rel="apple-touch-icon" href="/door-icon.png">
</head>

<body>
        <center>
                <h1>Apertura de valla y puerta</h1>
                <br>
                <h2>Abre la puerta, niña</h2>
                <br>
                <button style="width:80%; height:40%; background-image: url('door.png'); background-repeat: no-repeat; background-position: center; background-size: cover" onclick="location.href='/cgi-bin/open_door_direct'" type="button"></button>
                <br><br><br>
                <h2>Abre la valla, niña</h2>
                <br>
                <button style="width:80%; height:40%; background-image: url('fence.png'); background-repeat: no-repeat; background-position: center; background-size: cover" onclick="location.href='/cgi-bin/open_fence_direct'" type="button"></button>
        </center>
</body>
</html>

La página permite abrir la puerta o la valla pulsando cada una de las dos imágenes. La acción asociada a cada imagen es el script de apertura que se describe a continuación. La página queda como muestra la imagen:

smartphone-automatic-home-entry

Código de apertura de puerta y valla:

#!/bin/bash
RELAY=1
LOCKERTIME=0.005
PAGETIME=2

# Ensure we don't leave locker opened
trap "gpio write $RELAY 1 ; > open_door_running; exit" ERR SIGINT SIGTERM

# Check if we are already running
if [ -s open_door_running ]
then
        echo "<html>"
        echo "<meta http-equiv=\"refresh\" content=\"$PAGETIME; url='../'\">"
        echo "<head>"
        echo "<title>Portero automático 0.3 Beta - Abriendo puerta</title>"
        echo "</head> <body> <center>"
        echo "<br><br><br><h1>Puerta ya abierta</h1></center>"
        echo "</body></html>"
else
        # Set token to avoid web page bouncing
        echo -n "1" > open_door_running
        # Ensure RELAY is in mode out
        gpio mode $RELAY out

        # Bring RELAY up
        gpio write $RELAY 0

        echo "<html>"
        echo "<meta http-equiv=\"refresh\" content=\"$PAGETIME; url='../'\">"
        echo "<head>"
        echo "<title>Portero automático 0.3 Beta - Abriendo puerta</title>"
        echo "</head> <body> <center>"
        echo "<br><br><br><h1>Abierto</h1></center>"
        echo "</body></html>"

        sleep $LOCKERTIME

        # Bring RELAY down
        gpio write $RELAY 1

        # Clear token
        > open_door_running
fi

Constante RELAY

La constante RELAY establece el pin sobre el que actuar según la nomenclatura de WiringPi. El script de apertura de puerta tiene RELAY=1 y el de apertura de valla RELAY=2.

Constante LOCKERTIME

La constante LOCKERTIME establece el tiempo de activación del relé en segundos. Se trata de dar el mínimo impulso posible para que el cerradero se active. El script de apertura de puerta tiene LOCKERTIME=0.005 y el de apertura de valla LOCKERTIME=0.03.

Constante PAGETIME

La constante PAGETIME establece en tiempo en segundos durante el que se muestra el mensaje de apertura de puerta antes de volver a la página desde donde los usuarios pueden abrir la puerta.

El programa impide que se pueda llamar a la apertura de una entrada de forma repetida en un intervalo de tiempo al mantenerse en una página de notificación durante PAGETIME segundos. También asegura que la finalización inesperada del script deje el relé en estado de reposo

Asterisk

Hice una instalación básica de Asterisk desde la última versión de código fuente disponible siguiendo las instrucciones de la documentación. A partir de ahí, modifiqué los ficheros para añadir lo estrictamente necesario para mi prueba. Estas son las líneas de configuración fundamentales de los distintos ficheros.

extensions.conf

[from-internal]
exten => 101,1,Dial(PJSIP/portero,20)
exten => 102,1,Dial(PJSIP/jaime,20)
exten => 103,1,Dial(PJSIP/c470ip,20)
exten => 100,1,Answer
exten => 100,n,Queue(portero-queue)
exten => 100,n,Hangup

Establece un plan de acción sencillo asignando números de llamada a clientes SIP. La extensión 100 se contesta de forma automática y se envía a la cola portero-queue, que veremos más tarde.

sip.conf

[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0

;Templates for the necessary config sections

[endpoint_internal](!)
type=endpoint
context=from-internal
disallow=all
allow=ulaw

[auth_userpass](!)
type=auth
auth_type=userpass

[aor_dynamic](!)
type=aor
max_contacts=2

;Definitions for our phones, using the templates above

[c470ip](endpoint_internal)
auth=c470ip
aors=c470ip
[c470ip](auth_userpass)
password=AQUI_VA_EL_USUARIO
username=AQUI_VA_EL_USUARIO
[c470ip](aor_dynamic)

[jaime](endpoint_internal)
auth=jaime
aors=jaime
[jaime](auth_userpass)
password=AQUI_VA_EL_USUARIO
username=AQUI_VA_EL_USUARIO
[jaime](aor_dynamic)

[portero](endpoint_internal)
auth=portero
aors=portero
[portero](auth_userpass)
password=AQUI_VA_EL_USUARIO
username=AQUI_VA_EL_USUARIO
[portero](aor_dynamic)

Define los clientes SIP y su autenticación. Para un montaje de producción habría mucho que corregir aquí, empezando por la autenticación.

queues.conf

[portero-queue]
context = from-internal
musiclass = default
strategy = ringall
member => PJSIP/jaime
member => PJSIP/c470ip

Establece el comportamiento de las llamadas que se realizan desde el portero automático. ringall llama simultáneamente a todos los miembros de la cola. El portero reproduce una música de espera hasta que algún terminal descuelgue. Un punto para mejorar es establecer un tiempo límite de espera y definir alguna acción posterior, por si no hay nadie en casa.

¡Ya tenemos nuestro portero automático con WiFi!

El portero propiamente dicho es un servicio que arranca con el sistema operativo y que realiza varias funciones:

#!/bin/bash
# Pins (Button=WiringPi 0, Physical 11; Relay=WiringPi 1, Physical 12)
BUTTON=0
# Wait time before hanging up the call
WAITTIME=90
# Exit error code when pjsua is already running
E_PJSUA=80

# Check if we are already running
if [ `pgrep pjsua` ]
then
        echo "PJSUA ALREADY RUNNING!!!"
        exit $E_PJSUA
else
        # Ensure pjsua exits when we exit
        trap "screen -S portero -X stuff 'q^M'; exit 0" ERR EXIT SIGINT SIGTERM

        # set mode to in
        gpio mode $BUTTON in
        # Pullup BUTTON pin
        gpio mode $BUTTON up

        # Register with PBX
        screen -S portero -d -m pjsua --config-file /etc/botonera.cfg
        # Pump up the volume
        screen -S portero -X stuff 'V^M'
        sleep 1
        screen -S portero -X stuff '5^M'
        sleep 1
        screen -S portero -X stuff '4^M'

        while true; do
                # wait in interrupt for pin to go low (if BUTTON has been pulled up)
                gpio wfi $BUTTON falling
                # Ensure the button has been pressed because otherwise power noise may launch a call
                if [ `gpio read $BUTTON` -eq 0 ]
                then
                        # Call internal phone
                        screen -S portero -X stuff 'm^M'
                        sleep 1
                        screen -S portero -X stuff 'sip:100@cerbero.intranet.gomezgarcia.net^M'
                        sleep $WAITTIME
                        screen -S portero -X stuff 'h^M'
                fi
        done
fi
  1. Verifica si el softphone pjsua está ya ejecutándose.
  2. Establece el pin que recibe la señal del pulsador en modo lectura y pull-up. De esta forma cuando se detecte una caída de tensión se deberá lanzar la llamada.
  3. Lanza el softphone pjsua en un terminal oculto mediante la utilidad screen. La configuración está en el fichero botonera.cfg:
--app-log-level 0
--log-file /var/log/botonera.log
--local-port=5061
--registrar sip:DNS_NAME_OF_RASPBERRY
--username portero
--password 1234
--id sip:portero@DNS_NAME_OF_RASPBERRY
--realm asterisk
--auto-answer 200
--no-tcp
--playback-dev 10
--capture-dev 10

4. Se lanzan comandos de texto a la sesión de pjsua para configurar el volumen del altavoz y micrófono del portero.

5. Se queda a la espera de una interrupción que indique una bajada de tensión en el pin asociado al pulsador. En ocasiones esta interrupción es activada por ruido eléctrico, de manera que se verifica que la tensión del pin son 0 voltios antes de lanzar una llamada a la extensión 100 enviando comandos de texto a la sesión de pjsua.

6. Al cabo del tiempo definido en WAITTIME (90 segundos) cuelga la llamada enviando una “h” a la sesión de pjsua y vuelve a esperar nuevas pulsaciones.

Así ha quedado el aspecto final del portero

Para el montaje final aproveché la caja exterior e interior del portero automático antiguo.

automatic entry phone with raspberry pi outside

La caja exterior me aportaba el altavoz, el micrófono y el pulsador ya integrados.

La caja interior, aunque no se adaptaba demasiado bien a los componentes, fue suficiente para hacer caber todos los elementos. A posteriori, para mejorar el volumen del altavoz, tuve que añadir un amplificador de audio. Utilicé uno de Sodial.

automatic entry phone raspberry pi inside

Qué lecciones he aprendido y siguientes pasos

Decidí construir este portero porque en mi casa no hay cableado desde el interior a la calle, pero sí tengo cobertura WiFi. En su momento, los porteros automáticos WiFi costaban más de 500€. En el tiempo que he tardado en desarrollar mi portero han salido a la venta algunos que cubren y superan los objetivos del mío por algo más de 100€. Aun así, me alegro de que no existieran cuando empecé. No tendrían el aliciente de lo creado por uno mismo.

La lección más importante que saco de esta experiencia es que no hay mal que por bien no venga. Una necesidad doméstica me ha motivado para diseñar y construir un producto propio y me ha permitido pasar unos buenos días aprendiendo y probando. Además, es chulo abrir una valla desde el móvil 🙂

Estas son algunas mejoras o pruebas que se podrían acometer:

1. Seguridad

  • Este es un desarrollo rápido y no expuesto a Internet. Para dar más consistencia al proyecto habría que hacer una buena revisión del código, configuraciones, mecanismos de autenticación, firewall del sistema operativo, etc.

2. Audio

  • Limitar el ruido de audio. El amplificador de audio que tuve que añadir a posteriori introduce algo de ruido. Conectar el transformador a tierra mejora el nivel de ruido, pero sigue teniendo algo. Quizá sea cuestión de utilizar componentes de mejor calidad o un altavoz de mayor volumen de manera que no sea necesario el amplificador.
  • Alimentar el amplificador de audio sólo cuando es necesario. Optimiza el consumo eléctrico y reduce el ruido cuando el sistema no está en uso.

3. Telefonía

  • Afinar la configuración de la cola a la que llama el portero: Finalización de llamada, opciones de mensajes en determinadas circunstancias, etc.
  • Convertir el código del portero a un programa compilado para evitar el uso de screen para llamadas a pjsua.

4. Funciones

  • Hacer el portero accesible desde Internet.
  • Añadir una cámara para convertirlo en video-portero.
  • Añadir reconocimiento facial para apertura de la puerta sin manos.

5. Desarrollo

  • Probar plataformas de IoT como Blynk o Ubidots. Hay muchos ejemplos similares a mi portero basados en Blynk disponibles en Internet.
  • Presentar una API de verdad, basada en estándares para integración con otros sistemas domóticos y asistentes virtuales. Quizá comunicar vía MQTT.

6. Hardware

  • Arreglar el cableado.
  • Hacer cajas a medida mediante impresión 3D.

¿Te ha gustado mi pequeño proyecto de IoT práctico? ¿Te gustaría hacerlo tú mismo? ¡Estaré encantado de ayudarte!

jaime gomez

Jaime Gómez García

Santander Global Tech

Experto en arquitectura de infraestructura IT y telecomunicaciones. Aprendo sobre Internet, redes y criptografía aplicada cada día desde mediados de los 90.

 

Otros posts