.. note::

    ¡Hola! Bienvenido a la comunidad de entusiastas de SunFounder para Raspberry Pi, Arduino y ESP32 en Facebook. Únete a otros apasionados y profundiza en el uso de Raspberry Pi, Arduino y ESP32.

    **¿Por qué unirte?**

    - **Soporte de expertos**: Resuelve problemas postventa y desafíos técnicos con la ayuda de nuestra comunidad y equipo.
    - **Aprende y comparte**: Intercambia consejos y tutoriales para mejorar tus habilidades.
    - **Avances exclusivos**: Obtén acceso anticipado a anuncios de nuevos productos y adelantos exclusivos.
    - **Descuentos especiales**: Disfruta de descuentos exclusivos en nuestros productos más recientes.
    - **Promociones festivas y sorteos**: Participa en sorteos y promociones de temporada.

    👉 ¿Listo para explorar y crear con nosotros? Haz clic en [|link_sf_facebook|] y únete hoy mismo.

.. _py_pi5_welcome:

3.1.2 Bienvenido
================

Introducción
----------------

En este proyecto, usaremos un sensor PIR para detectar el movimiento de los 
peatones y utilizaremos servos, LED y un timbre para simular el funcionamiento 
de la puerta automática en una tienda de conveniencia. Cuando un peatón aparezca 
dentro del rango de detección del PIR, se encenderá la luz indicadora, la puerta 
se abrirá y el timbre reproducirá una melodía de bienvenida.

Componentes necesarios
-------------------------

En este proyecto, necesitamos los siguientes componentes.

.. image:: ../python_pi5/img/4.1.8_welcome_list.png
    :width: 800
    :align: center

.. Es definitivamente conveniente comprar un kit completo; aquí tienes el enlace:

.. .. list-table::
..     :widths: 20 20 20
..     :header-rows: 1

..     *   - Nombre
..         - ELEMENTOS EN ESTE KIT
..         - ENLACE
..     *   - Kit Raphael
..         - 337
..         - |link_Raphael_kit|

.. También puedes comprarlos por separado en los enlaces a continuación.

.. .. list-table::
..     :widths: 30 20
..     :header-rows: 1

..     *   - INTRODUCCIÓN AL COMPONENTE
..         - ENLACE DE COMPRA

..     *   - :ref:`gpio_extension_board`
..         - |link_gpio_board_buy|
..     *   - :ref:`breadboard`
..         - |link_breadboard_buy|
..     *   - :ref:`wires`
..         - |link_wires_buy|
..     *   - :ref:`resistor`
..         - |link_resistor_buy|
..     *   - :ref:`led`
..         - |link_led_buy|
..     *   - :ref:`pir`
..         - \-
..     *   - :ref:`servo`
..         - |link_servo_buy|
..     *   - :ref:`Buzzer`
..         - |link_passive_buzzer_buy|
..     *   - :ref:`transistor`
..         - |link_transistor_buy|


Diagrama esquemático
-------------------------

============ ======== ======== ===
T-Board Name physical wiringPi BCM
GPIO18       Pin 12   1        18
GPIO17       Pin 11   0        17
GPIO27       Pin 13   2        27
GPIO22       Pin 15   3        22
============ ======== ======== ===

.. image:: ../python_pi5/img/4.1.8_welcome_schematic.png
   :align: center

Procedimientos experimentales
----------------------------------

**Paso 1:** Construye el circuito.

.. image:: ../python_pi5/img/4.1.8_welcome_circuit.png
    :align: center

**Paso 2:** Cambia al directorio del código.

.. raw:: html

   <run></run>

.. code-block::

    cd ~/davinci-kit-for-raspberry-pi/python-pi5

**Paso 3:** Ejecuta el programa.

.. raw:: html

   <run></run>

.. code-block::

    sudo python3 3.1.2_Welcome.py

Después de ejecutar el código, si el sensor PIR detecta que alguien pasa, 
la puerta se abrirá automáticamente (simulada por el servo), se encenderá 
la luz indicadora y sonará la música de bienvenida. Después de que termine 
la música, el sistema cerrará automáticamente la puerta y apagará la luz, 
esperando a la próxima persona que pase.

El módulo PIR tiene dos potenciómetros: uno para ajustar la sensibilidad y 
otro para ajustar la distancia de detección. Para mejorar el funcionamiento 
del módulo PIR, gíralos ambos en sentido contrario a las agujas del reloj hasta el final.

.. image:: ../python_pi5/img/4.1.8_PIR_TTE.png
    :width: 400
    :align: center

.. warning::

    Si aparece el mensaje de error ``RuntimeError: Cannot determine SOC peripheral base address``, consulta :ref:`faq_soc`

**Código**

.. note::
    Puedes **Modificar/Restablecer/Copiar/Ejecutar/Detener** el código a continuación. Pero antes, debes ir a la ruta del código fuente como ``raphael-kit/python-pi5``. Después de modificar el código, puedes ejecutarlo directamente para ver el efecto.

.. raw:: html

    <run></run>

.. code-block:: python

   #!/usr/bin/env python3

   from gpiozero import LED, MotionSensor, Servo, TonalBuzzer
   import time

   # Configuración de pines GPIO para el LED, sensor de movimiento (PIR) y buzzer
   ledPin = LED(6)
   pirPin = MotionSensor(21)
   buzPin = TonalBuzzer(27)

   # Factor de corrección de ancho de pulso del servo y cálculo
   myCorrection = 0.45
   maxPW = (2.0 + myCorrection) / 1000  # Ancho de pulso máximo
   minPW = (1.0 - myCorrection) / 1000  # Ancho de pulso mínimo

   # Inicializar el servo con anchos de pulso personalizados
   servoPin = Servo(25, min_pulse_width=minPW, max_pulse_width=maxPW)

   # Melodía para el buzzer, con notas y duraciones
   tune = [('C#4', 0.2), ('D4', 0.2), (None, 0.2),
           ('Eb4', 0.2), ('E4', 0.2), (None, 0.6),
           ('F#4', 0.2), ('G4', 0.2), (None, 0.6),
           ('Eb4', 0.2), ('E4', 0.2), (None, 0.2),
           ('F#4', 0.2), ('G4', 0.2), (None, 0.2),
           ('C4', 0.2), ('B4', 0.2), (None, 0.2),
           ('F#4', 0.2), ('G4', 0.2), (None, 0.2),
           ('B4', 0.2), ('Bb4', 0.5), (None, 0.6),
           ('A4', 0.2), ('G4', 0.2), ('E4', 0.2), 
           ('D4', 0.2), ('E4', 0.2)]

   def setAngle(angle):
       """
       Move the servo to a specified angle.
       :param angle: Angle in degrees (0-180).
       """
       value = float(angle / 180)  # Convierte el ángulo a valor de servo
       servoPin.value = value      # Ajusta la posición del servo
       time.sleep(0.001)           # Breve pausa para el movimiento del servo

   def doorbell():
       """
       Play a musical tune using the buzzer.
       """
       for note, duration in tune:
           buzPin.play(note)       # Reproduce la nota
           time.sleep(float(duration))  # Duración de la nota
       buzPin.stop()               # Detiene el buzzer después de la melodía

   def closedoor():
       # Apaga el LED y mueve el servo para cerrar la puerta
       ledPin.off()
       for i in range(180, -1, -1):
           setAngle(i)             # Mueve el servo de 180 a 0 grados
           time.sleep(0.001)       # Breve pausa para un movimiento suave
       time.sleep(1)               # Espera después de cerrar la puerta

   def opendoor():
       # Enciende el LED, abre la puerta (mueve el servo), reproduce la melodía y luego cierra la puerta
       ledPin.on()
       for i in range(0, 181):
           setAngle(i)             # Mueve el servo de 0 a 180 grados
           time.sleep(0.001)       # Breve pausa para un movimiento suave
       time.sleep(1)               # Espera antes de reproducir la melodía
       doorbell()                  # Reproduce la melodía de bienvenida
       closedoor()                 # Cierra la puerta después de la melodía

   def loop():
       # Bucle principal para verificar movimiento y operar la puerta
       while True:
           if pirPin.motion_detected:
               opendoor()               # Abre la puerta si se detecta movimiento
           time.sleep(0.1)              # Breve pausa en el bucle

   try:
       loop()
   except KeyboardInterrupt:
       # Limpia los GPIO en caso de interrupción del usuario (e.g., Ctrl+C)
       buzPin.stop()
       ledPin.off()


**Explicación del Código**

#. El script comienza importando los módulos necesarios. La biblioteca ``gpiozero`` se usa para interactuar con el LED, el sensor de movimiento, el servo y el zumbador tonal. El módulo ``time`` permite manejar funciones relacionadas con el tiempo.

   .. code-block:: python

       #!/usr/bin/env python3
       from gpiozero import LED, MotionSensor, Servo, TonalBuzzer
       import time

#. Inicializa el LED, el sensor de movimiento PIR y el zumbador tonal en sus respectivos pines GPIO.

   .. code-block:: python

       # Configuración de pines GPIO para LED, sensor de movimiento (PIR) y zumbador
       ledPin = LED(6)
       pirPin = MotionSensor(21)
       buzPin = TonalBuzzer(27)

#. Calcula los anchos de pulso máximo y mínimo para el servo, incorporando un factor de corrección para ajustes precisos.

   .. code-block:: python

       # Factor de corrección y cálculo de ancho de pulso para el servo
       myCorrection = 0.45
       maxPW = (2.0 + myCorrection) / 1000  # Ancho de pulso máximo
       minPW = (1.0 - myCorrection) / 1000  # Ancho de pulso mínimo

#. Inicializa el motor del servo en el pin GPIO 25 con los anchos de pulso personalizados para una posición precisa.

   .. code-block:: python

       # Inicializar el servo con anchos de pulso personalizados
       servoPin = Servo(25, min_pulse_width=minPW, max_pulse_width=maxPW)

#. La melodía se define como una secuencia de notas (frecuencia) y duraciones (segundos).

   .. code-block:: python

       # Melodía para el zumbador, con notas y duraciones
       tune = [('C#4', 0.2), ('D4', 0.2), (None, 0.2),
               ('Eb4', 0.2), ('E4', 0.2), (None, 0.6),
               ('F#4', 0.2), ('G4', 0.2), (None, 0.6),
               ('Eb4', 0.2), ('E4', 0.2), (None, 0.2),
               ('F#4', 0.2), ('G4', 0.2), (None, 0.2),
               ('C4', 0.2), ('B4', 0.2), (None, 0.2),
               ('F#4', 0.2), ('G4', 0.2), (None, 0.2),
               ('B4', 0.2), ('Bb4', 0.5), (None, 0.6),
               ('A4', 0.2), ('G4', 0.2), ('E4', 0.2), 
               ('D4', 0.2), ('E4', 0.2)]

#. Función para mover el servo a un ángulo específico. Convierte el ángulo a un valor entre 0 y 1 para el servo.

   .. code-block:: python

       def setAngle(angle):
           """
           Move the servo to a specified angle.
           :param angle: Angle in degrees (0-180).
           """
           value = float(angle / 180)  # Convierte el ángulo a valor de servo
           servoPin.value = value      # Establece la posición del servo
           time.sleep(0.001)           # Breve pausa para el movimiento del servo

#. Función para reproducir una melodía utilizando el zumbador. Itera a través de la lista ``tune``, reproduciendo cada nota durante su duración especificada.

   .. code-block:: python

       def doorbell():
           """
           Play a musical tune using the buzzer.
           """
           for note, duration in tune:
               buzPin.play(note)       # Reproduce la nota
               time.sleep(float(duration))  # Duración de la nota
           buzPin.stop()               # Detiene el zumbador después de la melodía

#. Funciones para abrir y cerrar la puerta utilizando el motor del servo. La función ``opendoor`` enciende el LED, abre la puerta, reproduce la melodía y luego cierra la puerta.

   .. code-block:: python

       def closedoor():
           # Apaga el LED y mueve el servo para cerrar la puerta
           ledPin.off()
           for i in range(180, -1, -1):
               setAngle(i)             # Mueve el servo de 180 a 0 grados
               time.sleep(0.001)       # Breve pausa para un movimiento suave
           time.sleep(1)               # Espera después de cerrar la puerta

       def opendoor():
           # Enciende el LED, abre la puerta (mueve el servo), reproduce la melodía y cierra la puerta
           ledPin.on()
           for i in range(0, 181):
               setAngle(i)             # Mueve el servo de 0 a 180 grados
               time.sleep(0.001)       # Breve pausa para un movimiento suave
           time.sleep(1)               # Espera antes de reproducir la melodía
           doorbell()                  # Reproduce la melodía de bienvenida
           closedoor()                 # Cierra la puerta después de la melodía

#. Bucle principal que verifica constantemente la detección de movimiento. Cuando se detecta movimiento, activa la función ``opendoor``.

   .. code-block:: python

       def loop():
           # Bucle principal para verificar movimiento y operar la puerta
           while True:
               if pirPin.motion_detected:
                   opendoor()               # Abre la puerta si se detecta movimiento
               time.sleep(0.1)              # Breve pausa en el bucle

#. Ejecuta el bucle principal y asegura que el script pueda detenerse con un comando de teclado (Ctrl+C), apagando el zumbador y el LED para una salida limpia.

   .. code-block:: python

       try:
           loop()
       except KeyboardInterrupt:
           # Limpia los GPIO en caso de interrupción del usuario (e.g., Ctrl+C)
           buzPin.stop()
           ledPin.off()

