.. note::

    こんにちは、SunFounderのRaspberry Pi & Arduino & ESP32愛好家コミュニティへようこそ！Facebook上でRaspberry Pi、Arduino、ESP32についてもっと深く掘り下げ、他の愛好家と交流しましょう。

    **参加する理由は？**

    - **エキスパートサポート**：コミュニティやチームの助けを借りて、販売後の問題や技術的な課題を解決します。
    - **学び＆共有**：ヒントやチュートリアルを交換してスキルを向上させましょう。
    - **独占的なプレビュー**：新製品の発表や先行プレビューに早期アクセスしましょう。
    - **特別割引**：最新製品の独占割引をお楽しみください。
    - **祭りのプロモーションとギフト**：ギフトや祝日のプロモーションに参加しましょう。

    👉 私たちと一緒に探索し、創造する準備はできていますか？[|link_sf_facebook|]をクリックして今すぐ参加しましょう！

.. _2.1.6_py_pi5:

2.1.6 ロータリーエンコーダーモジュール
================================================

はじめに
------------

このプロジェクトでは、ロータリーエンコーダーについて学びます。ロータリーエンコーダーは、厳密なタイミングのシーケンスで一連の定期的なパルスを持つ電子スイッチです。ICと併用すると、増加、減少、ページ送りなどの操作や、マウスのスクロール、メニュー選択などが実現できます。

必要な部品
------------------------------

このプロジェクトには、次のコンポーネントが必要です。 

.. image:: ../python_pi5/img/2.1.6_rotary_encoder_list.png

一式を購入するのが便利です、こちらがリンクです： 

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

    *   - 名前	
        - このキットのアイテム
        - リンク
    *   - Raphael Kit
        - 337
        - |link_Raphael_kit|

以下のリンクから別々に購入することもできます。

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

    *   - コンポーネントの紹介
        - 購入リンク

    *   - :ref:`cpn_gpio_board`
        - |link_gpio_board_buy|
    *   - :ref:`cpn_breadboard`
        - |link_breadboard_buy|
    *   - :ref:`cpn_wires`
        - |link_wires_buy|
    *   - :ref:`cpn_rotary_encoder`
        - |link_rotary_encoder_buy|

回路図
------------------------
.. image:: ../python_pi5/img/2.1.6_rotary_encoder_schematic.png
   :align: center



実験手順
-----------------------

**ステップ 1:** 回路を組み立てる。

.. image:: ../python_pi5/img/2.1.6_rotary_encoder_circuit.png

この例では、ロータリーエンコーダーのピンをブレッドボードと40ピンケーブルを使用してラズベリーパイに直接接続し、ロータリーエンコーダーのGNDをGNDに、「+」を5Vに、SWをデジタルGPIO27に、DTをデジタルGPIO18に、CLKをデジタルGPIO17に接続します。

**ステップ 2:** コードファイルを開く。

.. raw:: html

   <run></run>

.. code-block::

    cd ~/raphael-kit/python-pi5

**ステップ 3:** 実行する。

.. raw:: html

   <run></run>

.. code-block::

    sudo python3 2.1.6_RotaryEncoder_zero.py

シェル上でカウントが表示されます。ロータリーエンコーダーを時計回りに回すと、カウントが増えます。反時計回りに回すと、カウントが減ります。ロータリーエンコーダーのスイッチを押すと、数値がゼロに戻ります。

.. warning::

    エラー メッセージ ``RuntimeError: Cannot determine SOC peripheral base address`` が表示された場合は、 :ref:`faq_soc` を参照してください。

**コード**

.. note::

   下記のコードを **変更/リセット/コピー/実行/停止** することができます。しかし、その前に ``raphael-kit/python-pi5`` のようなソースコードのパスに移動する必要があります。コードを変更した後、直接実行して効果を見ることができます。


.. raw:: html

    <run></run>

.. code-block:: python

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

   # Initialize the rotary encoder and button
   encoder = RotaryEncoder(a=17, b=18)  # Rotary Encoder connected to GPIO pins 17 (CLK) and 18 (DT)
   button = Button(27)                  # Button connected to GPIO pin 27

   global_counter = 0  # Track the rotary encoder's position

   def rotary_change():
      """ Update the global counter based on the rotary encoder's rotation. """
      global global_counter
      global_counter += encoder.steps  # Adjust counter based on encoder steps
      encoder.steps = 0  # Reset encoder steps after updating counter
      print('Global Counter =', global_counter)  # Display current counter value

   def reset_counter():
      """ Reset the global counter to zero when the button is pressed. """
      global global_counter
      global_counter = 0  # Reset the counter
      print('Counter reset')  # Indicate counter reset

   # Assign the reset_counter function to button press event
   button.when_pressed = reset_counter

   try:
      # Monitor rotary encoder continuously and process changes
      while True:
         rotary_change()  # Handle rotary encoder changes
         sleep(0.1)  # Short delay to reduce CPU load

   except KeyboardInterrupt:
      # Gracefully handle a keyboard interrupt (Ctrl+C)
      pass

**コード解析**

#. ``gpiozero`` ライブラリから ``RotaryEncoder`` と ``Button`` クラスをインポートし、遅延のために ``sleep`` 関数を使用します。

   .. code-block:: python

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

#. GPIOピン17と18にロータリーエンコーダーを、GPIOピン27にボタンを初期化します。

   .. code-block:: python

      # Initialize the rotary encoder and button
      encoder = RotaryEncoder(a=17, b=18)  # Rotary Encoder connected to GPIO pins 17 (CLK) and 18 (DT)
      button = Button(27)                  # Button connected to GPIO pin 27

#. ロータリーエンコーダーの位置を追跡するために ``global_counter`` グローバル変数を宣言します。

   .. code-block:: python

      global_counter = 0  # Track the rotary encoder's position

#. ロータリーエンコーダーの回転に基づいてグローバルカウンターを更新する ``rotary_change`` 関数を定義します。

   .. code-block:: python

      def rotary_change():
         """ Update the global counter based on the rotary encoder's rotation. """
         global global_counter
         global_counter += encoder.steps  # Adjust counter based on encoder steps
         encoder.steps = 0  # Reset encoder steps after updating counter
         print('Global Counter =', global_counter)  # Display current counter value

#. ボタンが押されたときにグローバルカウンターをゼロにリセットする ``reset_counter`` 関数を定義します。

   .. code-block:: python

      def reset_counter():
         """ Reset the global counter to zero when the button is pressed. """
         global global_counter
         global_counter = 0  # Reset the counter
         print('Counter reset')  # Indicate counter reset

#. ボタンが押されたときに呼び出されるように ``reset_counter`` 関数を割り当てます。

   .. code-block:: python

      # Assign the reset_counter function to button press event
      button.when_pressed = reset_counter

#. 継続的なループ内でスクリプトは ``rotary_change`` を呼び出してロータリーエンコーダーの変更を処理し、CPU負荷を減らすために短い遅延を導入します。KeyboardInterruptsを優雅に処理するためにtry-exceptブロックを使用します。

   .. code-block:: python

      try:
         # Monitor rotary encoder continuously and process changes
         while True:
            rotary_change()  # Handle rotary encoder changes
            sleep(0.1)  # Short delay to reduce CPU load

      except KeyboardInterrupt:
         # Gracefully handle a keyboard interrupt (Ctrl+C)
         pass


