 
.. note::

    Bonjour et bienvenue dans la Communauté Facebook des passionnés de Raspberry Pi, Arduino et ESP32 de SunFounder ! Plongez plus profondément dans l'univers des Raspberry Pi, Arduino et ESP32 avec d'autres passionnés.

    **Pourquoi rejoindre ?**

    - **Support d'experts** : Résolvez les problèmes après-vente et les défis techniques avec l'aide de notre communauté et de notre équipe.
    - **Apprendre et partager** : Échangez des astuces et des tutoriels pour améliorer vos compétences.
    - **Aperçus exclusifs** : Accédez en avant-première aux annonces de nouveaux produits et aux aperçus.
    - **Réductions spéciales** : Profitez de réductions exclusives sur nos produits les plus récents.
    - **Promotions festives et cadeaux** : Participez à des cadeaux et des promotions de vacances.

    👉 Prêt à explorer et à créer avec nous ? Cliquez [|link_sf_facebook|] et rejoignez-nous aujourd'hui !

.. _4.1.7_py_pi5:

4.1.4 Dispositif de Comptage
================================

Introduction
---------------

Nous allons réaliser un système de compteur à affichage numérique, composé d'un capteur 
PIR et d'un afficheur à 4 chiffres. Lorsque le capteur PIR détecte le passage de quelqu'un, 
le nombre affiché sur l'afficheur à 4 chiffres augmente de 1. Vous pouvez utiliser ce compteur 
pour compter le nombre de personnes traversant le passage.


Composants nécessaires
----------------------

Pour ce projet, nous avons besoin des composants suivants :

.. image:: ../python_pi5/img/4.1.7_counting_device_list_1.png
    :align: center

.. image:: ../python_pi5/img/4.1.7_counting_device_list_2.png
    :align: center

Il est certainement pratique d'acheter un kit complet, voici le lien :

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

    *   - Nom
        - ÉLÉMENTS DANS CE KIT
        - LIEN
    *   - Kit Raphael
        - 337
        - |link_Raphael_kit|

Vous pouvez également les acheter séparément via les liens ci-dessous.

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

    *   - INTRODUCTION AUX COMPOSANTS
        - LIEN D'ACHAT

    *   - :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_4_digit`
        - \-
    *   - :ref:`cpn_74hc595`
        - |link_74hc595_buy|
    *   - :ref:`cpn_pir`
        - \-


Schéma
-----------

============ ======== ======== ===
Nom T-Board  Physique 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
GPIO26       Pin 37   25       26
============ ======== ======== ===

.. image:: ../python_pi5/img/4.1.7_counting_device_schematic.png
   :align: center

Procédures Expérimentales
-----------------------------

**Étape 1** : Construisez le circuit.

.. image:: ../python_pi5/img/4.1.7_counting_device_circuit.png


**Étape 2** : Allez dans le dossier du code.

.. raw:: html

   <run></run>

.. code-block::

    cd ~/raphael-kit/python-pi5

**Étape 3** : Exécutez le fichier exécutable.

.. raw:: html

   <run></run>

.. code-block::

    sudo python3 4.1.7_CountingDevice_zero.py

Après l'exécution du code, lorsque le capteur PIR détecte le passage de quelqu'un, 
le nombre affiché sur l'afficheur à 4 chiffres augmentera de 1.

Il y a deux potentiomètres sur le module PIR : l'un pour régler la sensibilité et l'autre pour régler la distance de détection. Pour que le module PIR fonctionne mieux, vous devez tourner les deux dans le sens inverse des aiguilles d'une montre jusqu'à la fin.

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

.. warning::

    Si vous recevez le message d'erreur ``RuntimeError: Cannot determine SOC peripheral base address``, veuillez consulter :ref:`faq_soc`

**Code**

.. note::
    Vous pouvez **Modifier/Réinitialiser/Copier/Exécuter/Arrêter** le code ci-dessous. Mais avant cela, vous devez accéder au chemin du code source comme ``raphael-kit/python-pi5``. Après avoir modifié le code, vous pouvez l'exécuter directement pour voir l'effet.

.. raw:: html

    <run></run>

.. code-block:: python

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

   # Initialize PIR motion sensor on GPIO 26
   pir = MotionSensor(26)

   # Initialize shift register pins
   SDI = OutputDevice(24)    # Serial Data Input
   RCLK = OutputDevice(23)   # Register Clock Input
   SRCLK = OutputDevice(18)  # Shift Register Clock Input

   # Initialize 7-segment display pins
   placePin = [OutputDevice(pin) for pin in (10, 22, 27, 17)]

   # Define digit codes for 7-segment display
   number = (0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90)

   # Counter for the displayed number
   counter = 0

   def clearDisplay():
       # Clears the display by setting all segments off
       for _ in range(8):
           SDI.on()
           SRCLK.on()
           SRCLK.off()
       RCLK.on()
       RCLK.off()

   def hc595_shift(data):
       # Shifts data into the 74HC595 shift register
       for i in range(8):
           SDI.value = 0x80 & (data << i)
           SRCLK.on()
           SRCLK.off()
       RCLK.on()
       RCLK.off()

   def pickDigit(digit):
       # Activates a specific digit of the 7-segment display
       for pin in placePin:
           pin.off()
       placePin[digit].on()

   def display():
       # Updates the display with the current counter value
       global counter
       clearDisplay()
       pickDigit(0)
       hc595_shift(number[counter % 10])

       clearDisplay()
       pickDigit(1)
       hc595_shift(number[counter % 100//10])

       clearDisplay()
       pickDigit(2)
       hc595_shift(number[counter % 1000//100])

       clearDisplay()
       pickDigit(3)
       hc595_shift(number[counter % 10000//1000])

   def loop():
       # Main loop to update display and check for motion
       global counter
       currentState = 0
       lastState = 0
       while True:
           display()
           currentState = 1 if pir.motion_detected else 0
           if currentState == 1 and lastState == 0:
               counter += 1
           lastState = currentState

   try:
       loop()
   except KeyboardInterrupt:
       # Turn off all pins when the script is interrupted
       SDI.off()
       SRCLK.off()
       RCLK.off()
       pass


**Explication du Code**

#. Cette ligne importe les classes ``OutputDevice`` et ``MotionSensor`` de la bibliothèque ``gpiozero``. ``OutputDevice`` peut être une LED, un moteur ou tout autre dispositif que vous souhaitez contrôler en sortie. Le ``MotionSensor`` est généralement un capteur PIR (infra-rouge passif) utilisé pour détecter les mouvements.

   .. code-block:: python

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

#. Initialise le capteur de mouvement PIR connecté à la broche GPIO 26.

   .. code-block:: python

       # Initialize PIR motion sensor on GPIO 26
       pir = MotionSensor(26)

#. Initialise les broches GPIO connectées à l'entrée de données série (SDI), à l'entrée de l'horloge du registre (RCLK) et à l'entrée de l'horloge du registre à décalage (SRCLK).

   .. code-block:: python

       # Initialize shift register pins
       SDI = OutputDevice(24)    # Serial Data Input
       RCLK = OutputDevice(23)   # Register Clock Input
       SRCLK = OutputDevice(18)  # Shift Register Clock Input

#. Initialise les broches pour chaque chiffre de l'afficheur 7 segments et définit les codes binaires pour afficher les chiffres de 0 à 9.

   .. code-block:: python

       # Initialize 7-segment display pins
       placePin = [OutputDevice(pin) for pin in (10, 22, 27, 17)]

       # Define digit codes for 7-segment display
       number = (0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90)

#. Efface l'affichage 7 segments en éteignant tous les segments avant d'afficher le chiffre suivant.

   .. code-block:: python

       def clearDisplay():
           # Clears the display by setting all segments off
           for _ in range(8):
               SDI.on()
               SRCLK.on()
               SRCLK.off()
           RCLK.on()
           RCLK.off()

#. Déplace un octet de données dans le registre à décalage 74HC595, contrôlant les segments de l'afficheur.

   .. code-block:: python

       def hc595_shift(data):
           # Shifts data into the 74HC595 shift register
           for i in range(8):
               SDI.value = 0x80 & (data << i)
               SRCLK.on()
               SRCLK.off()
           RCLK.on()
           RCLK.off()

#. Sélectionne quel chiffre de l'afficheur 7 segments activer. Chaque chiffre est contrôlé par une broche GPIO distincte.

   .. code-block:: python

       def pickDigit(digit):
           # Activates a specific digit of the 7-segment display
           for pin in placePin:
               pin.off()
           placePin[digit].on()

#. Initialise l'affichage pour le chiffre des unités en premier, suivi de l'activation de l'affichage pour le chiffre des dizaines. Ensuite, engage les affichages pour les centaines et les milliers dans cet ordre. Cette succession rapide d'activations crée l'illusion d'un affichage continu à quatre chiffres.

   .. code-block:: python

       def display():
           # Updates the display with the current counter value
           global counter
           clearDisplay()
           pickDigit(0)
           hc595_shift(number[counter % 10])

           clearDisplay()
           pickDigit(1)
           hc595_shift(number[counter % 100//10])

           clearDisplay()
           pickDigit(2)
           hc595_shift(number[counter % 1000//100])

           clearDisplay()
           pickDigit(3)
           hc595_shift(number[counter % 10000//1000])

#. Définit la boucle principale où l'affichage est continuellement mis à jour et l'état du capteur PIR est vérifié. Si un mouvement est détecté, le compteur est incrémenté.

   .. code-block:: python

       def loop():
           # Main loop to update display and check for motion
           global counter
           currentState = 0
           lastState = 0
           while True:
               display()
               currentState = 1 if pir.motion_detected else 0
               if currentState == 1 and lastState == 0:
                   counter += 1
               lastState = currentState

#. Exécute la boucle principale et s'assure que le script peut être interrompu avec une commande clavier (Ctrl+C), éteignant toutes les broches pour une sortie propre.

   .. code-block:: python

       try:
           loop()
       except KeyboardInterrupt:
           # Turn off all pins when the script is interrupted
           SDI.off()
           SRCLK.off()
           RCLK.off()
           pass



