 
.. 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.14_py_pi5:

4.1.11 Verrou à Code
================================

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

Dans ce projet, nous utiliserons un clavier et un écran LCD pour créer un verrou à 
combinaison. Le LCD affichera une invite correspondante pour que vous puissiez taper 
votre mot de passe sur le clavier. Si le mot de passe est correctement saisi, "Correct" 
sera affiché.

Sur la base de ce projet, nous pouvons ajouter des composants électroniques supplémentaires, 
tels qu'un buzzer, une LED, etc., pour ajouter différents phénomènes expérimentaux lors de la 
saisie du mot de passe.

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

Dans ce projet, nous avons besoin des composants suivants.

.. image:: ../python_pi5/img/4.1.14_password_lock_list.png
    :width: 800
    :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_i2c_lcd`
        - |link_i2clcd1602_buy|
    *   - :ref:`cpn_keypad`
        - \-

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

============ ======== ======== ===
Nom T-Board  physique wiringPi BCM
GPIO18       Pin 12   1        18
GPIO23       Pin 16   4        23
GPIO24       Pin 18   5        24
GPIO25       Pin 22   6        25
GPIO17       Pin 11   0        17
GPIO27       Pin 13   2        27
GPIO22       Pin 15   3        22
SPIMOSI      Pin 19   12       10
SDA1         Pin 3             
SCL1         Pin 5             
============ ======== ======== ===

.. image:: ../python_pi5/img/4.1.14_password_lock_schematic.png
   :align: center

Procédures expérimentales
-----------------------------

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

.. image:: ../python_pi5/img/4.1.14_password_lock_circuit.png

**Étape 2 :** Changez de répertoire.

.. raw:: html

   <run></run>

.. code-block:: 

    cd ~/raphael-kit/python-pi5

**Étape 3 :** Exécutez le programme.

.. raw:: html

   <run></run>

.. code-block:: 

    sudo python3 4.1.14_PasswordLock_zero.py

Une fois le programme exécuté, utilisez le clavier pour saisir le mot de 
passe : 1984. Si "CORRECT" apparaît sur le LCD1602, le mot de passe est correct ; 
sinon, "WRONG KEY" apparaîtra.

.. note::

    * Si vous obtenez l'erreur ``FileNotFoundError: [Errno 2] No such file or directory: '/dev/i2c-1'``, vous devez consulter :ref:`i2c_config` pour activer l'I2C.
    * Si vous obtenez l'erreur ``ModuleNotFoundError: No module named 'smbus2'``, veuillez exécuter ``sudo pip3 install smbus2``.
    * Si l'erreur ``OSError: [Errno 121] Remote I/O error`` apparaît, cela signifie que le module est mal câblé ou endommagé.
    * Si le code et le câblage sont corrects, mais que le LCD n'affiche toujours pas de contenu, vous pouvez tourner le potentiomètre à l'arrière pour augmenter le contraste.


.. 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 DigitalOutputDevice, Button
   from time import sleep
   import LCD1602

   class Keypad:
       def __init__(self, rows_pins, cols_pins, keys):
           """
           Initialize the Keypad with specified row and column pins and keys.
           :param rows_pins: List of GPIO pins for the rows.
           :param cols_pins: List of GPIO pins for the columns.
           :param keys: List of keys in the keypad layout.
           """
           self.rows = [DigitalOutputDevice(pin) for pin in rows_pins]  # Row pins setup
           self.cols = [Button(pin, pull_up=False) for pin in cols_pins]  # Column pins setup
           self.keys = keys  # Keypad key layout

       def read(self):
           """
           Read and return a list of keys that are currently pressed.
           :return: List of pressed keys.
           """
           pressed_keys = []
           for i, row in enumerate(self.rows):
               row.on()  # Activate current row
               for j, col in enumerate(self.cols):
                   if col.is_pressed:
                       index = i * len(self.cols) + j
                       pressed_keys.append(self.keys[index])
               row.off()  # Deactivate row after checking
           return pressed_keys

   # Password verification setup
   LENS = 4
   password = ['1', '9', '8', '4']  # Preset password
   testword = ['0', '0', '0', '0']  # User input storage
   keyIndex = 0  # Index for input keys

   def check():
       """
       Check if the entered password matches the preset password.
       :return: 1 if match, 0 otherwise.
       """
       for i in range(LENS):
           if password[i] != testword[i]:
               return 0
       return 1

   def setup():
       """
       Setup the keypad and LCD display.
       """
       global keypad, last_key_pressed
       # Pin configuration for keypad
       rows_pins = [18, 23, 24, 25]
       cols_pins = [10, 22, 27, 17]
       keys = ["1", "2", "3", "A",
               "4", "5", "6", "B",
               "7", "8", "9", "C",
               "*", "0", "#", "D"]

       # Initialize keypad and LCD
       keypad = Keypad(rows_pins, cols_pins, keys)
       last_key_pressed = []
       LCD1602.init(0x27, 1)  # Initialize LCD
       LCD1602.clear()
       LCD1602.write(0, 0, 'WELCOME!')
       LCD1602.write(2, 1, 'Enter password')
       sleep(2)

   def loop():
       """
       Main loop for handling keypad input and password verification.
       """
       global keyIndex, LENS, keypad, last_key_pressed
       while True:
           pressed_keys = keypad.read()
           if pressed_keys and pressed_keys != last_key_pressed:
               if keyIndex < LENS:
                   LCD1602.clear()
                   LCD1602.write(0, 0, "Enter password:")
                   LCD1602.write(15 - keyIndex, 1, pressed_keys[0])
                   testword[keyIndex] = pressed_keys[0]
                   keyIndex += 1

               if keyIndex == LENS:
                   if check() == 0:
                       LCD1602.clear()
                       LCD1602.write(3, 0, "WRONG KEY!")
                       LCD1602.write(0, 1, "please try again")
                   else:
                       LCD1602.clear()
                       LCD1602.write(4, 0, "CORRECT!")
                       LCD1602.write(2, 1, "welcome back")
                   keyIndex = 0  # Reset key index after checking

           last_key_pressed = pressed_keys
           sleep(0.1)

   try:
       setup()
       loop()
   except KeyboardInterrupt:
       LCD1602.clear()  # Clear LCD display on interrupt


**Explication du Code**


#. Le script importe des classes pour gérer les dispositifs de sortie numérique et les boutons de la bibliothèque gpiozero. Il importe également la fonction sleep du module time, permettant d'ajouter des délais dans l'exécution du script. De plus, la bibliothèque LCD1602 est importée pour contrôler l'affichage LCD1602.

   .. code-block:: python

       #!/usr/bin/env python3
       from gpiozero import DigitalOutputDevice, Button
       from time import sleep
       import LCD1602

#. Définit une classe personnalisée pour gérer le clavier. Elle initialise le clavier avec des broches de ligne et de colonne spécifiées et fournit une méthode ``read`` pour détecter les touches pressées.

   .. code-block:: python

       class Keypad:
           def __init__(self, rows_pins, cols_pins, keys):
               """
               Initialize the Keypad with specified row and column pins and keys.
               :param rows_pins: List of GPIO pins for the rows.
               :param cols_pins: List of GPIO pins for the columns.
               :param keys: List of keys in the keypad layout.
               """
               self.rows = [DigitalOutputDevice(pin) for pin in rows_pins]  # Row pins setup
               self.cols = [Button(pin, pull_up=False) for pin in cols_pins]  # Column pins setup
               self.keys = keys  # Keypad key layout

           def read(self):
               """
               Read and return a list of keys that are currently pressed.
               :return: List of pressed keys.
               """
               pressed_keys = []
               for i, row in enumerate(self.rows):
                   row.on()  # Activate current row
                   for j, col in enumerate(self.cols):
                       if col.is_pressed:
                           index = i * len(self.cols) + j
                           pressed_keys.append(self.keys[index])
                   row.off()  # Deactivate row after checking
               return pressed_keys

#. Configure le système de vérification du mot de passe. ``LENS`` définit la longueur du mot de passe. ``password`` est le mot de passe correct prédéfini, tandis que ``testword`` est utilisé pour stocker la saisie de l'utilisateur. ``keyIndex`` suit la position actuelle dans la saisie de l'utilisateur.

   .. code-block:: python

       # Password verification setup
       LENS = 4
       password = ['1', '9', '8', '4']  # Preset password
       testword = ['0', '0', '0', '0']  # User input storage
       keyIndex = 0  # Index for input keys

#. Fonction pour comparer le mot de passe saisi (``testword``) avec le mot de passe prédéfini (``password``) et retourner le résultat.

   .. code-block:: python

       def check():
           """
           Check if the entered password matches the preset password.
           :return: 1 if match, 0 otherwise.
           """
           for i in range(LENS):
               if password[i] != testword[i]:
                   return 0
           return 1

#. Initialise le clavier et l'affichage LCD. Affiche un message de bienvenue et des instructions pour entrer le mot de passe.

   .. code-block:: python

       def setup():
           """
           Setup the keypad and LCD display.
           """
           global keypad, last_key_pressed
           # Pin configuration for keypad
           rows_pins = [18, 23, 24, 25]
           cols_pins = [10, 22, 27, 17]
           keys = ["1", "2", "3", "A",
                   "4", "5", "6", "B",
                   "7", "8", "9", "C",
                   "*", "0", "#", "D"]

           # Initialize keypad and LCD
           keypad = Keypad(rows_pins, cols_pins, keys)
           last_key_pressed = []
           LCD1602.init(0x27, 1)  # Initialize LCD
           LCD1602.clear()
           LCD1602.write(0, 0, 'WELCOME!')
           LCD1602.write(2, 1, 'Enter password')
           sleep(2)

#. La boucle principale pour gérer la saisie du clavier et la vérification du mot de passe. Elle met à jour l'affichage LCD en fonction du mot de passe saisi et fournit un retour d'information si le mot de passe est correct ou incorrect.

   .. code-block:: python

       def loop():
           """
           Main loop for handling keypad input and password verification.
           """
           global keyIndex, LENS, keypad, last_key_pressed
           while True:
               pressed_keys = keypad.read()
               if pressed_keys and pressed_keys != last_key_pressed:
                   if keyIndex < LENS:
                       LCD1602.clear()
                       LCD1602.write(0, 0, "Enter password:")
                       LCD1602.write(15 - keyIndex, 1, pressed_keys[0])
                       testword[keyIndex] = pressed_keys[0]
                       keyIndex += 1

                   if keyIndex == LENS:
                       if check() == 0:
                           LCD1602.clear()
                           LCD1602.write(3, 0, "WRONG KEY!")
                           LCD1602.write(0, 1, "please try again")
                       else:
                           LCD1602.clear()
                           LCD1602.write(4, 0, "CORRECT!")
                           LCD1602.write(2, 1, "welcome back")
                       keyIndex = 0  # Reset key index after checking

               last_key_pressed = pressed_keys
               sleep(0.1)

#. Exécute la configuration et entre dans la boucle principale. Permet une sortie propre du programme en utilisant une interruption clavier (Ctrl+C), en effaçant l'affichage LCD.

   .. code-block:: python

       try:
           setup()
           loop()
       except KeyboardInterrupt:
           LCD1602.clear()  # Clear LCD display on interrupt

