.. note::

    ¡Hola, bienvenido a la comunidad de entusiastas de SunFounder Raspberry Pi & Arduino & ESP32 en Facebook! Sumérgete más en Raspberry Pi, Arduino y ESP32 con otros entusiastas.

    **¿Por qué unirte?**

    - **Soporte Experto**: 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.
    - **Preestrenos Exclusivos**: Obtén acceso anticipado a anuncios de nuevos productos y avances exclusivos.
    - **Descuentos Especiales**: Disfruta de descuentos exclusivos en nuestros productos más recientes.
    - **Promociones y Sorteos Festivos**: Participa en sorteos y promociones de temporada.

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

.. _4.1.12_py_pi5:

4.1.9 Semáforo
==========================

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

En este proyecto, utilizaremos luces LED de tres colores para simular el
cambio de semáforos, y una pantalla de 7 segmentos de cuatro dígitos se
utilizará para mostrar la cuenta regresiva de cada estado del semáforo.

Componentes Necesarios
---------------------------------

En este proyecto, necesitamos los siguientes componentes.

.. image:: ../python_pi5/img/4.1.12_traffic_light_list.png
    :width: 800
    :align: center

Es definitivamente conveniente comprar un kit completo, aquí está el enlace: 

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

    *   - Nombre	
        - ARTÍCULOS 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 DEL COMPONENTE
        - ENLACE DE COMPRA

    *   - :ref:`cpn_gpio_extension_board`
        - |link_gpio_board_buy|
    *   - :ref:`cpn_breadboard`
        - |link_breadboard_buy|
    *   - :ref:`cpn_wires`
        - |link_wires_buy|
    *   - :ref:`cpn_resistor`
        - |link_resistor_buy|
    *   - :ref:`cpn_led`
        - |link_led_buy|
    *   - :ref:`cpn_4_digit`
        - \-
    *   - :ref:`cpn_74hc595`
        - |link_74hc595_buy|


Diagrama Esquemático
-------------------------

============== ====== ======== ===
Nombre T-Board física WiringPi BCM
GPIO17         Pin 11 0        17
GPIO27         Pin 13 2        27
GPIO22         Pin 15 3        22
SPIMOSI        Pin 19 12       10
GPIO18         Pin 12 1        18
GPIO23         Pin 16 4        23
GPIO24         Pin 18 5        24
GPIO25         Pin 22 6        25
SPICE0         Pin 24 10       8
SPICE1         Pin 26 11       7
============== ====== ======== ===

.. image:: ../python_pi5/img/4.1.12_traffic_light_schematic.png
   :align: center

Procedimientos Experimentales
---------------------------------

**Paso 1:** Construye el circuito.

.. image:: ../python_pi5/img/4.1.12_traffic_light_circuit.png

**Paso 2:** Cambia de directorio.

.. raw:: html

   <run></run>

.. code-block::

    cd ~/raphael-kit/python-pi5

**Paso 3:** Ejecuta.

.. raw:: html

   <run></run>

.. code-block::

    sudo python3 4.1.12_TrafficLight_zero.py

Cuando el código se ejecute, los LEDs simularán el cambio de color de los
semáforos. Primero, el LED rojo se encenderá durante 60s, luego el LED
verde se encenderá durante 30s; después, el LED amarillo se encenderá
durante 5s. Luego de esto, el LED rojo se encenderá nuevamente durante
60s. De esta manera, esta serie de acciones se ejecutará repetidamente.
Mientras tanto, la pantalla de 7 segmentos de cuatro dígitos mostrará la
cuenta regresiva continuamente.

Código
----------

.. note::
    Puedes **Modificar/Restablecer/Copiar/Ejecutar/Detener** el código a continuación. Pero antes de eso, necesitas 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 OutputDevice, LED
   import threading

   # Configurar pines GPIO para el registro de desplazamiento 74HC595
   SDI = OutputDevice(24)   # Entrada de datos en serie
   RCLK = OutputDevice(23)  # Reloj del registro
   SRCLK = OutputDevice(18) # Reloj del registro de desplazamiento

   # Configurar pines GPIO para la selección de dígitos en la pantalla de 7 segmentos
   placePin = [OutputDevice(pin) for pin in (10, 22, 27, 17)]

   # Códigos de segmentos para los números 0-9 en la pantalla de 7 segmentos
   number = (0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90)

   # Configurar pines GPIO para los LEDs del semáforo
   ledPinR = LED(25) # LED Rojo
   ledPinG = LED(8)  # LED Verde
   ledPinY = LED(7)  # LED Amarillo

   # Configuración de duraciones para los semáforos
   greenLight = 30
   yellowLight = 5
   redLight = 60

   # Nombres de colores de semáforos
   lightColor = ("Red", "Green", "Yellow")

   # Inicializar variables de estado
   colorState = 0
   counter = 60
   timer1 = None

   def setup():
       """ Inicializar el sistema de semáforos y comenzar el temporizador. """
       global timer1
       timer1 = threading.Timer(1.0, timer)
       timer1.start()

   def clearDisplay():
       """ Limpiar la pantalla de 7 segmentos. """
       for _ in range(8):
           SDI.on()
           SRCLK.on()
           SRCLK.off()
       RCLK.on()
       RCLK.off()

   def hc595_shift(data):
       """ Desplazar datos al registro de desplazamiento 74HC595 para la visualización de dígitos. """
       for i in range(8):
           SDI.value = 0x80 & (data << i)
           SRCLK.on()
           SRCLK.off()
       RCLK.on()
       RCLK.off()

   def pickDigit(digit):
       """ Seleccionar un dígito específico para mostrar en la pantalla de 7 segmentos. """
       for pin in placePin:
           pin.off()
       placePin[digit].on()

   def timer():
       """ Manejar el temporizador para los cambios de semáforos. """
       global counter, colorState, timer1
       timer1 = threading.Timer(1.0, timer)
       timer1.start()
       counter -= 1
       if counter == 0:
           counter = [greenLight, yellowLight, redLight][colorState]
           colorState = (colorState + 1) % 3
       print(f"contador : {counter}    color: {lightColor[colorState]}")

   def lightup():
       """ Actualizar el LED del semáforo según el estado actual. """
       global colorState
       ledPinR.off()
       ledPinG.off()
       ledPinY.off()
       [ledPinR, ledPinG, ledPinY][colorState].on()

   def display():
       """ Mostrar el valor actual del contador en la pantalla de 7 segmentos. """
       global counter

       for i in range(4):
           digit = counter // (10 ** (3 - i)) % 10
           if i == 0 and digit == 0:
               continue
           clearDisplay()
           pickDigit(3 - i)
           hc595_shift(number[digit])

   def loop():
       """ Bucle principal para actualizar continuamente la pantalla y las luces. """
       while True:
           display()
           lightup()

   def destroy():
       """ Limpiar los recursos cuando el script se termina. """
       global timer1
       timer1.cancel()
       ledPinR.off()
       ledPinG.off()
       ledPinY.off()

   try:
       setup()
       loop()
   except KeyboardInterrupt:
       destroy()



Explicación del Código
-------------------------------

#. Importa las clases ``OutputDevice`` y ``LED`` de la biblioteca gpiozero, permitiendo el control de dispositivos de salida generales y específicamente de los LEDs. Importa el módulo threading de Python, que se utilizará para crear y manejar hilos para la ejecución concurrente.

   .. code-block:: python

       #!/usr/bin/env python3
       from gpiozero import OutputDevice, LED
       import threading

#. Inicializa los pines GPIO conectados a la Entrada de Datos en Serie (SDI), la Entrada de Reloj del Registro (RCLK) y la Entrada de Reloj del Registro de Desplazamiento (SRCLK) del registro de desplazamiento.

   .. code-block:: python

       # Configurar pines GPIO para el registro de desplazamiento 74HC595
       SDI = OutputDevice(24)   # Entrada de Datos en Serie
       RCLK = OutputDevice(23)  # Reloj del Registro
       SRCLK = OutputDevice(18) # Reloj del Registro de Desplazamiento

#. Inicializa los pines para cada dígito de la pantalla de 7 segmentos y define los códigos binarios para mostrar los números del 0 al 9.

   .. code-block:: python

       # Configurar pines GPIO para la selección de dígitos en la pantalla de 7 segmentos
       placePin = [OutputDevice(pin) for pin in (10, 22, 27, 17)]

       # Códigos de segmentos para los números 0-9 en la pantalla de 7 segmentos
       number = (0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90)

#. Inicializa los pines GPIO para los LEDs Rojo, Verde y Amarillo utilizados en la simulación del semáforo. Establece la duración (en segundos) para cada estado de color en el sistema de semáforo. Define los nombres de los colores del semáforo para referencia.

   .. code-block:: python

       # Configurar pines GPIO para los LEDs del semáforo
       ledPinR = LED(25) # LED Rojo
       ledPinG = LED(8)  # LED Verde
       ledPinY = LED(7)  # LED Amarillo

       # Configuración de duraciones para los semáforos
       greenLight = 30
       yellowLight = 5
       redLight = 60

       # Nombres de colores del semáforo
       lightColor = ("Red", "Green", "Yellow")

#. Inicializa variables para rastrear el estado actual del color, un contador para el tiempo y un marcador de posición para un objeto de temporizador.

   .. code-block:: python

       # Inicializar variables de estado
       colorState = 0
       counter = 60
       timer1 = None

#. Inicializa el sistema de semáforos y comienza el hilo del temporizador.

   .. code-block:: python

       def setup():
           """ Inicializar el sistema de semáforos y comenzar el temporizador. """
           global timer1
           timer1 = threading.Timer(1.0, timer)
           timer1.start()

#. Funciones para controlar la pantalla de 7 segmentos. ``clearDisplay`` apaga todos los segmentos, ``hc595_shift`` desplaza datos al registro de desplazamiento y ``pickDigit`` activa un dígito específico en la pantalla.

   .. code-block:: python

       def clearDisplay():
           """ Clear the 7-segment display. """
           for _ in range(8):
               SDI.on()
               SRCLK.on()
               SRCLK.off()
           RCLK.on()
           RCLK.off()

       def hc595_shift(data):
           """ Shift data to the 74HC595 shift register for digit display. """
           for i in range(8):
               SDI.value = 0x80 & (data << i)
               SRCLK.on()
               SRCLK.off()
           RCLK.on()
           RCLK.off()

       def pickDigit(digit):
           """ Select a specific digit to display on the 7-segment display. """
           for pin in placePin:
               pin.off()
           placePin[digit].on()

#. Maneja el temporizador para los cambios de semáforos y actualiza el contador y el estado del color.

   .. code-block:: python

       def timer():
           """ Handle the timing for traffic light changes. """
           global counter, colorState, timer1
           timer1 = threading.Timer(1.0, timer)
           timer1.start()
           counter -= 1
           if counter == 0:
               counter = [greenLight, yellowLight, redLight][colorState]
               colorState = (colorState + 1) % 3
           print(f"counter : {counter}    color: {lightColor[colorState]}")

#. Actualiza el estado de los LEDs del semáforo según el estado actual del color.

   .. code-block:: python

       def lightup():
           """ Actualizar el LED del semáforo según el estado actual. """
           global colorState
           ledPinR.off()
           ledPinG.off()
           ledPinY.off()
           [ledPinR, ledPinG, ledPinY][colorState].on()

#. Calcula el dígito a mostrar en cada segmento de la pantalla de 7 segmentos y lo actualiza en consecuencia.

   .. code-block:: python

       def display():
           """ Mostrar el valor actual del contador en la pantalla de 7 segmentos. """
           global counter

           for i in range(4):
               digit = counter // (10 ** (3 - i)) % 10
               if i == 0 and digit == 0:
                   continue
               clearDisplay()
               pickDigit(3 - i)
               hc595_shift(number[digit])

#. El bucle principal que actualiza continuamente la pantalla y los LEDs del semáforo.

   .. code-block:: python

       def loop():
           """ Bucle principal para actualizar continuamente la pantalla y las luces. """
           while True:
               display()
               lightup()

#. Limpia los recursos cuando se termina el script, como apagar los LEDs y detener el hilo del temporizador.

   .. code-block:: python

       def destroy():
           """ Clean up resources when the script is terminated. """
           global timer1
           timer1.cancel()
           ledPinR.off()
           ledPinG.off()
           ledPinY.off()



