.. include:: /index.rst :start-after: start_hello_message :end-before: end_hello_message .. _py_fun_count: 4.6 カウント装置 ======================= **はじめに** このプロジェクトでは、PIR モーションセンサーと 4 桁 7 セグメントディスプレイを使用して、人の通過数をカウントするシステムを作成します。PIR センサーが動きを検出するたびに、表示されるカウント値が 1 ずつ増加します。このプロジェクトは、廊下や入口の通行量を監視する用途に適しており、GPIO プログラミングと表示制御を実践的に学ぶことができます。 ---------------------------------------------- **必要なもの** このプロジェクトに必要なコンポーネントは以下のとおりです。 .. list-table:: :widths: 30 20 :header-rows: 1 * - COMPONENT INTRODUCTION - PURCHASE LINK * - :ref:`cpn_breadboard` - |link_breadboard_buy| * - :ref:`cpn_wires` - |link_wires_buy| * - :ref:`cpn_resistor` - |link_resistor_buy| * - :ref:`cpn_pir` - |link_pir_buy| * - :ref:`cpn_4_digit` - \- * - :ref:`cpn_74hc595` - |link_74hc595_buy| * - :ref:`cpn_fusion_hat` - \- * - Raspberry Pi - \- ---------------------------------------------- **回路図** 以下はこのプロジェクトの回路図です。 .. image:: img/fzz/4.1.4_sch.png :width: 100% :align: center ---------------------------------------------- **配線図** 以下の配線図に従って、各コンポーネントを正しく接続してください。 .. image:: img/fzz/4.1.4_bb.png :width: 80% :align: center .. note:: 最適な感度と検出距離を得るために、PIR モジュール上の 2 つの可変抵抗を調整してください。最良の結果を得るには、両方を反時計回りいっぱいまで回してください。 .. image:: ../python/img/PIR_TTE.png :width: 300 :align: center ---------------------------------------------- **サンプルの実行** このチュートリアルで使用するすべてのサンプルコードは ``ai-lab-kit`` ディレクトリに含まれています。 以下の手順に従ってサンプルを実行してください。 .. raw:: html .. code-block:: shell cd ~/ai-lab-kit/python/ sudo python3 4.6_CountingDevice.py この Python スクリプトは、PIR モーションセンサーと 74HC595 シフトレジスタで駆動する 7 セグメントディスプレイを組み合わせたものです。このスクリプトは次のように動作します。 1. **動きの検出**: GPIO22 に接続された PIR モーションセンサーが動きを検出します。動きを 1 回検出するごとにカウンターが 1 増加します。 2. **数字の表示**: - 現在のカウント値が 4 桁の 7 セグメントディスプレイに表示されます。 - 動きが検出されるたびに表示が更新され、数値が増加します。 3. **継続的な監視**: スクリプトは動きを継続的に監視し、表示をリアルタイムで更新します。 4. **安全な終了**: ``Ctrl+C`` を押すと、すべてのピンがオフになり、スクリプトは安全に終了します。 ---------------------------------------------- **コード** 以下は、このプロジェクトで使用する Python スクリプトです。 .. raw:: html .. code-block:: python #!/usr/bin/env python3 from fusion_hat.pin import Pin, Mode, Pull # Initialize PIR motion sensor on GPIO 22 (input with pull-down) pir = Pin(22, mode=Mode.IN, pull=Pull.DOWN) # Define GPIO pins for the 74HC595 shift register SDI = Pin(17, mode=Mode.OUT) # Serial Data Input RCLK = Pin(4, mode=Mode.OUT) # Register Clock (latch) SRCLK = Pin(27, mode=Mode.OUT) # Shift Register Clock # Define GPIO pins used to select one of the 4 digits on the 7-segment display placePin = [Pin(pin, mode=Mode.OUT) for pin in (23, 24, 25, 12)] # Segment code table for digits 0–9 (common-cathode 7-segment display) number = (0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90) # Counter to display the number of detections counter = 0 def clearDisplay(): """Clear the 7-segment display by shifting out 'all off' bits.""" for _ in range(8): SDI.high() # Send a high bit SRCLK.high() # Clock it in SRCLK.low() RCLK.high() # Latch output RCLK.low() def hc595_shift(data): """Shift a full byte of data into the 74HC595 to control the segments.""" for i in range(8): SDI.value(0x80 & (data << i)) # Output next bit of the data SRCLK.high() # Pulse shift clock SRCLK.low() RCLK.high() # Latch data to display RCLK.low() def pickDigit(digit): """Enable one specific digit (0–3) on the multiplexed display.""" for pin in placePin: pin.low() # Disable all digits placePin[digit].high() # Activate the selected digit def display(): """Display the current 4-digit counter value on the 7-segment display.""" global counter # Display ones place clearDisplay() pickDigit(3) hc595_shift(number[counter % 10]) # Display tens place clearDisplay() pickDigit(2) hc595_shift(number[counter % 100 // 10]) # Display hundreds place clearDisplay() pickDigit(1) hc595_shift(number[counter % 1000 // 100]) # Display thousands place clearDisplay() pickDigit(0) hc595_shift(number[counter % 10000 // 1000]) def loop(): """Main loop: continuously update display and detect PIR motion transitions.""" global counter currentState = 0 lastState = 0 while True: display() # Refresh 7-segment display currentState = 1 if pir.value() == 1 else 0 # Read PIR output # Detect rising edge: motion detected now, but not in last cycle if currentState == 1 and lastState == 0: counter += 1 # Increase the count lastState = currentState # Save state for next loop try: loop() # Start main loop except KeyboardInterrupt: # Clean up GPIO pins when exiting SDI.low() SRCLK.low() RCLK.low() pass ---------------------------------------------- **コードの解説** 1. **動きの検出:** PIR センサーが動きを検出すると、その信号によってカウンターが増加します。 2. **表示の更新:** ``hc595_shift`` 関数と ``pickDigit`` 関数が連携して 7 セグメントディスプレイを制御し、カウンター値をリアルタイムで更新します。 3. **メインループ:** ``loop`` 関数は PIR センサーを継続的に監視し、動きが検出されると表示を更新します。 4. **割り込み処理:** スクリプトは中断時にすべての GPIO ピンを安全にオフにし、予期しない動作を防ぎます。 ---------------------------------------------- **トラブルシューティング** 1. **ディスプレイが動作しない**: - **原因**: 7 セグメントディスプレイまたはシフトレジスタの配線が正しくありません。 - **対処方法**: - GPIO ピンとシフトレジスタの SDI、SRCLK、RCLK ピンの接続を確認してください。 - すべての ``placePin`` の接続が対応する桁ピンと一致していることを確認してください。 2. **モーションセンサーが反応しない**: - **原因**: PIR センサーの配線ミス、または周囲環境による干渉。 - **対処方法**: - PIR センサーが GPIO22、電源、GND に正しく接続されていることを確認してください。 - 必要に応じて、PIR センサー上の感度および遅延時間調整用の可変抵抗を調整してください。 3. **カウンターが増加しない**: - **原因**: モーションセンサーの状態変化が正しく検出されていません。 - **対処方法**: - ``pir.value()`` の値が正しく更新されているか確認してください。 - ``loop()`` 関数にデバッグ用の ``print`` を追加して、状態遷移を確認してください。 4. **表示がちらつく**: - **原因**: 表示更新のタイミングが適切でない、または遅延が不足しています。 - **対処方法**: ``display()`` 関数に小さな待機時間を追加して表示を安定させてください。 .. code-block:: python import time time.sleep(0.01) ---------------------------------------------- **拡張アイデア** 1. **双方向カウンター**: もう 1 つ PIR センサーを追加して反対方向の動きを検出し、カウンターを減算できるようにします。 .. code-block:: python pir2 = Pin(16, Pin.IN, pull= Pin.PULL_DOWN) if pir2.value()==1: counter -= 1 2. **しきい値アラート**: カウンターがあらかじめ設定した値を超えたときに、LED やブザーなどで警告を出します。 .. code-block:: python from fusion_hat import Buzzer buzzer = Buzzer(Pin(22)) if counter > 50: buzzer.on() else: buzzer.off() 3. **データログ記録**: カウンター値とタイムスタンプをファイルへ保存して分析できます。 .. code-block:: python with open("motion_log.txt", "a") as log_file: log_file.write(f"{time.time():.3f}, Counter: {counter}\n") 4. **タイマーによるリセット**: 一定時間動きがない場合にカウンターをリセットします。 .. code-block:: python last_motion_time = time.time() if time.time() - last_motion_time > 300: # 5 minutes counter = 0 5. **イベント連動の動作**: カウンターが特定の値に達したときに、家電の制御など特定の動作を実行します。 ---------------------------------------------- **まとめ** このプロジェクトでは、PIR センサーと 7 セグメントディスプレイを使用した動き検出と表示制御の基本を学びます。より高度な IoT やデータ可視化プロジェクトへ発展させるための、実践的な基礎となります。