注釈

こんにちは、SunFounder Raspberry Pi & Arduino & ESP32 Enthusiast Community on Facebookへようこそ!他の愛好家と一緒に、Raspberry Pi、Arduino、ESP32の世界により深く入り込みましょう。

参加する理由

  • 専門家サポート: 購入後の問題や技術的な課題を、コミュニティと私たちのチームの助けを借りて解決します。

  • 学習と共有: ヒントやチュートリアルを交換して、スキルを向上させましょう。

  • 限定プレビュー: 新製品の発表や先行プレビューに早期アクセスできます。

  • 特別割引: 最新製品を特別割引でお楽しみいただけます。

  • 季節限定キャンペーンとプレゼント: プレゼント企画やホリデーキャンペーンに参加しましょう。

👉 一緒に発見し、創造する準備はできましたか? [こちら] をクリックして、今すぐ参加しましょう!

2.12 ロータリーエンコーダー

はじめに

ロータリーエンコーダーは、回転運動をデジタル信号に変換する入力デバイスです。メニューの操作、値の調整、項目のスクロールなどによく使用されます。このプロジェクトでは、プッシュボタン内蔵のロータリーエンコーダーをFusion HAT+経由でRaspberry Piに接続し、回転ステップを読み取り、エンコーダーのスイッチでカウンターをリセットする方法を学びます。

この実験では、イベントコールバックを使用してロータリーエンコーダーを扱う方法を示し、低遅延で滑らかな入力処理を実現します。


必要なもの

このプロジェクトに必要なコンポーネントは以下のとおりです。

COMPONENT INTRODUCTION

PURCHASE LINK

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

購入

ジャンパーワイヤー

購入

Fusion HAT+

-

Raspberry Pi

-


回路図

以下はこのプロジェクトの回路図です。

../_images/2.1.6_rotary_sch.png

配線図

以下の配線図を参考にして接続してください。

../_images/2.1.6_rotary_bb.png

このサンプルで使用するピン割り当て

  • CLKGPIO 17

  • DTGPIO 4

  • SW(ボタン)GPIO 27 (内部プルアップ使用)

  • +3.3V

  • GNDGND

すべての接続が確実であることを確認してください。エンコーダーのスイッチ端子が C(COM)NO/NC に分かれている場合は、 C をGNDに、 NO をSWピンに接続してください(ソフトウェア側で内部プルアップを有効にしています)。


サンプルの実行

このチュートリアルで使用するすべてのサンプルコードは ai-lab-kit ディレクトリに含まれています。 以下の手順に従ってサンプルを実行してください。

cd ~/ai-lab-kit/python/
sudo python3 2.12_RotaryEncoder.py

スクリプトを実行すると、ロータリーエンコーダーを回すたびにカウンターが増減し、現在のカウンター値がリアルタイムでコンソールに表示されます。エンコーダーのボタンを押すとカウンターは0にリセットされ、リセットメッセージが表示されます。プログラムは Ctrl + C を押して終了するまで動作し続け、ユーザー入力に応答します。


コード

以下は、このプロジェクトで使用するPythonコードです。

#!/usr/bin/env python3

from fusion_hat.pin import Pin, Mode, Pull
import time

# GPIO pins (BCM numbering)
CLK_PIN = 17
DT_PIN  = 4
SW_PIN  = 27

# Initialize pins with internal pull-ups
clk = Pin(CLK_PIN, mode=Mode.IN, pull=Pull.UP)
dt  = Pin(DT_PIN,  mode=Mode.IN, pull=Pull.UP)
sw  = Pin(SW_PIN,  mode=Mode.IN, pull=Pull.UP)  # Button is active LOW

raw = 0                 # Raw quadrature transitions
last_clk = clk.value()  # Previous CLK state
last_detent = None      # Last displayed detent value

print("Rotate the knob. Press the button to reset. CTRL + C to exit.")
try:
   while True:
      c = clk.value()
      if c != last_clk:
            # Direction: DT != CLK means one direction, else the other
            raw += 1 if dt.value() != c else -1

            # Most encoders generate 2 transitions per detent (click)
            detent = raw // 2

            # Only update output when the detent value changes
            if detent != last_detent:
               print(f"\rCounter: {detent}   ", end="", flush=True)
               last_detent = detent

            last_clk = c

      # Reset when button is pressed
      if sw.value() == 0:
            raw = 0
            detent = 0
            print("\rCounter: 0   ", end="", flush=True)
            last_detent = 0
            time.sleep(0.25)  # Button debounce

      time.sleep(0.001)  # Polling interval (1 ms)

except KeyboardInterrupt:
   print("\nExit")

コードの解説

  1. インポート

    from fusion_hat.pin import Pin, Mode, Pull
    import time
    
    • Pin は、ロータリーエンコーダー用のGPIOピンを設定・読み取りするために使用します。

    • ModePull は、ピンの入出力設定および内部プルアップ抵抗の設定に使用します。

    • time は、ポーリング間隔やボタンのデバウンス時間を制御するために使用します。

  2. GPIOピンの設定

    CLK_PIN = 17
    DT_PIN  = 4
    SW_PIN  = 27
    
    clk = Pin(CLK_PIN, mode=Mode.IN, pull=Pull.UP)
    dt  = Pin(DT_PIN,  mode=Mode.IN, pull=Pull.UP)
    sw  = Pin(SW_PIN,  mode=Mode.IN, pull=Pull.UP)
    
    • CLKDT は、ロータリーエンコーダーの2つの位相信号ピンです。

    • SW は、エンコーダー内蔵のプッシュボタンです。

    • 内部プルアップ抵抗により、エンコーダーが操作されていないときでも信号を安定させます。

    • ボタンはアクティブLOWで、押されると 0 を読み取ります。

  3. 状態変数

    raw = 0
    last_clk = clk.value()
    last_detent = None
    
    • raw は、エンコーダーの低レベルな位相信号の変化回数を記録します。

    • last_clk は、信号変化を検出するために前回のCLK状態を保持します。

    • last_detent は、同じ値の再表示を防ぐために最後に表示したカウンター値を記録します。

  4. 回転の検出

    c = clk.value()
    if c != last_clk:
        raw += 1 if dt.value() != c else -1
    
    • スクリプトはCLKピンを継続的にポーリングします。

    • CLKの状態変化は、エンコーダーが回転したことを意味します。

    • 回転方向は、DTとCLKの状態を比較して判定します。

  5. デテント(クリック)の計算

    detent = raw // 2
    
    • 多くのロータリーエンコーダーは、物理的な1クリックごとに2回の信号変化を出力します。

    • そのため、2で割ることで生の変化回数を実際のクリック数に変換しています。

  6. 見やすいコンソール出力

    if detent != last_detent:
        print(f"\\rCounter: {detent}   ", end="", flush=True)
    
    • カウンター値が変化したときだけ表示を更新します。

    • \\r はカーソルを行頭に戻し、その場で表示を更新するために使用します。

    • flush=True により、端末へ即座に表示を反映します。

  7. ボタンによるリセット処理

    if sw.value() == 0:
        raw = 0
        detent = 0
        print("\\rCounter: 0   ", end="", flush=True)
        time.sleep(0.25)
    
    • ボタンを押すとカウンターが0にリセットされます。

    • チャタリングによる多重入力を防ぐため、短い待機時間を入れています。

  8. ポーリング間隔とプログラム終了

    time.sleep(0.001)
    
    • 1msのポーリング間隔により、応答性とCPU負荷のバランスを取っています。

    • Ctrl + C を押すことで、プログラムを安全に終了できます。


トラブルシューティング

  1. 回しても出力されない

    • 原因: CLK/DTの配線ミス、またはGND接続不良。

    • 対処方法: CLK→GPIO17DT→GPIO4GND共通 を確認してください。電源は3.3Vを使用し、5V出力のエンコーダーをGPIOに直接接続しないでください。

  2. カウンター値が不安定に飛ぶ(チャタリング/ノイズ)

    • 原因: 機械的なチャタリング、または長い配線によるノイズ。

    • 対処方法: 配線はできるだけ短くし、可能であればツイストペアにしてください。必要に応じてCLK/DTとGNDの間に小容量コンデンサ(例:0.01〜0.1 µF)を追加してください。また、使用しているライブラリにデバウンス機能があれば有効にしてください。

  3. ボタンが検出されない

    • 原因: スイッチ接点がGNDではなく3.3V側に接続されている、またはプルアップ設定が誤っている。

    • 対処方法: ボタンを押したときに SWGND に接続される構成になっていることを確認し、pull=Pin.PULL_UP が設定されていることを確認してください。

  4. 回転方向が逆になる

    • 原因: CLKとDTの接続が意図した方向と逆になっている。

    • 対処方法: CLKDT の配線を入れ替えるか、アプリケーション側で増減方向を反転させてください。


拡張アイデア

  1. 変数を滑らかに調整する(例:音量/明るさ)

    value = 50  # 0..100
    def rotary_change():
        global value
        value = max(0, min(100, 50 + encoder.steps()))
        print(f'Value = {value}')
    
  2. ボタンでモードを切り替える

    mode = ['Fine', 'Coarse']
    idx = 0
    def reset_counter():
        global idx
        idx = 1 - idx
        encoder.reset()
        print(f'Mode switched to: {mode[idx]}')
    
  3. LED/ブザーによるフィードバック

    from fusion_hat import Pin
    led = Pin(26, Pin.OUT)
    def rotary_change():
        print('Counter =', encoder.steps())
        led.on()
        # brief flash without blocking callbacks is recommended
        led.off()
    
  4. 長押しで特別な動作を実行する

    • sw.when_activated や(利用可能であれば) sw.when_deactivated でタイムスタンプを記録し、短押しと長押しで異なる動作を実装できます。


まとめ

この実験では、Fusion HAT+上でロータリーエンコーダーとその内蔵ボタンを使用し、イベント駆動型のコールバックで入力を読み取る方法を学びました。この方法を使えば、Raspberry Piプロジェクトにおいて、メニュー操作、ダイヤル式の設定変更、リアルタイム制御用のジョグホイールなど、応答性の高いインターフェースを構築できます。