.. include:: /index.rst
:start-after: start_hello_message
:end-before: end_hello_message
.. _py_fun_10s:
4.8 SPIEL - 10-Sekunden-Challenge
=================================
**Einführung**
Testen Sie Ihre Konzentration und Ihr Timing mit dem Spiel „10-Sekunden-Challenge“! In diesem Projekt bauen Sie einen Zauberstab, indem Sie einen Neigungsschalter an einem Stab befestigen. Schütteln Sie den Zauberstab, um einen Timer auf einer 4-stelligen Segmentanzeige zu starten. Schütteln Sie ihn erneut, um den Timer zu stoppen. Das Ziel ist es, den Timer so nah wie möglich bei **10,00 Sekunden** zu stoppen. Treten Sie gegen Freunde an und finden Sie heraus, wer der wahre Zeitzauberer ist!
----------------------------------------------
**Was Sie benötigen**
Nachfolgend finden Sie die Liste der für dieses Projekt erforderlichen Komponenten:
.. list-table::
:widths: 30 20
:header-rows: 1
* - KOMPONENTENBESCHREIBUNG
- KAUFLINK
* - :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_tilt_switch`
- \-
* - :ref:`cpn_fusion_hat`
- \-
* - Raspberry Pi
- \-
----------------------------------------------
**Schaltplan**
Nachfolgend ist der schematische Schaltplan für die „10-Sekunden-Challenge“ dargestellt:
.. image:: img/fzz/4.1.15_sch.png
:width: 100%
:align: center
----------------------------------------------
**Verdrahtungsdiagramm**
Folgen Sie den unten gezeigten Verdrahtungsanweisungen, um die Schaltung aufzubauen:
.. image:: img/fzz/4.1.15_bb.png
:width: 80%
:align: center
----------------------------------------------
**Beispiel ausführen**
Der gesamte in diesem Tutorial verwendete Beispielcode befindet sich im Verzeichnis ``ai-lab-kit``.
Führen Sie die folgenden Schritte aus, um das Beispiel auszuführen:
.. raw:: html
.. code-block:: shell
cd ~/ai-lab-kit/python/
sudo python3 4.8_GAME_10Second.py
Wenn das Skript ausgeführt wird:
* Schütteln Sie den Zauberstab, um den Timer auf der 4-stelligen Segmentanzeige zu starten.
* Schütteln Sie den Zauberstab erneut, um den Timer zu stoppen.
* Wenn der Timer 10,00 Sekunden anzeigt, gewinnen Sie die Runde!
* Schütteln Sie den Zauberstab noch einmal, um das Spiel zurückzusetzen und eine neue Runde zu starten.
----------------------------------------------
**Code**
Hier ist der Python-Code für dieses Projekt:
.. raw:: html
.. code-block:: python
#!/usr/bin/env python3
# Import Pin control, modes, and pull-up/down definitions
from fusion_hat.pin import Pin, Mode, Pull
# Import time for delays
import time
# Import threading to use Timer for repeated callbacks
import threading
# Initialize the button connected to GPIO 22, set as input with pull-down resistor
sensorPin = 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 controlling digit selection on the 4-digit 7-segment display
placePin = [Pin(pin, mode=Mode.OUT) for pin in (23, 24, 25, 12)]
# Define the segment encoding for digits 0–9 (common cathode)
number = (0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90)
# Counter value, timer object, and game state variable
counter = 0
timer1 = None
gameState = 0
def clearDisplay():
"""Clear all segments by shifting out 'all off' bits to the 74HC595."""
for _ in range(8):
SDI.on() # Send high bits (turned off segments)
SRCLK.on() # Pulse shift clock
SRCLK.off()
RCLK.on() # Latch the data
RCLK.off()
def hc595_shift(data):
"""Shift out one byte to the 74HC595 to control segment lighting."""
for i in range(8):
SDI.value(0x80 & (data << i)) # Output next bit
SRCLK.on() # Clock pulse
SRCLK.off()
RCLK.on() # Latch data to output
RCLK.off()
def pickDigit(digit):
"""Enable one of the 4 digits on the display by activating its control pin."""
for pin in placePin:
pin.off() # Disable all digits
placePin[digit].on() # Enable selected digit
def display():
"""Render the current 4-digit counter value onto the 7-segment display."""
global counter
# Units digit
clearDisplay()
pickDigit(3)
hc595_shift(number[counter % 10])
# Tens digit
clearDisplay()
pickDigit(2)
hc595_shift(number[counter % 100 // 10])
# Hundreds digit (minus 0x80 to enable decimal point if needed)
clearDisplay()
pickDigit(1)
hc595_shift(number[counter % 1000 // 100] - 0x80)
# Thousands digit
clearDisplay()
pickDigit(0)
hc595_shift(number[counter % 10000 // 1000])
def stateChange():
"""Handle button-triggered mode changes: start or stop the timer."""
global gameState, counter, timer1
# When gameState = 0 → Reset counter and start timer
if gameState == 0:
counter = 0 # Reset counter
time.sleep(1) # Small delay before start
timer() # Start counting
# When gameState = 1 → Stop the timer
elif gameState == 1 and timer1 is not None:
timer1.cancel() # Stop Timer thread
time.sleep(1)
# Toggle between state 0 and 1
gameState = (gameState + 1) % 2
def loop():
"""Main loop: refresh the display and detect button presses."""
global counter
currentState = 0
lastState = 0
while True:
display() # Continuously update display
currentState = sensorPin.value() # Read button state
# Detect falling edge: button released → pressed transition
if (currentState == 0) and (lastState == 1):
stateChange() # Trigger state change
lastState = currentState # Save state for edge detection
def timer():
"""Timer callback: increments counter every 0.01 seconds using threading.Timer."""
global counter, timer1
timer1 = threading.Timer(0.01, timer) # Create next timer event
timer1.start() # Start timer loop
counter += 1 # Increase counter value
try:
loop() # Run main loop
except KeyboardInterrupt:
if timer1:
timer1.cancel() # Cleanly stop timer on exit
----------------------------------------------
**Code verstehen**
1. **Tastereingabe:**
Der Neigungsschalter ist mit GPIO-Pin 22 verbunden und erkennt, wenn der Zauberstab geschüttelt wird.
2. **74HC595-Schieberegister:**
Das Schieberegister steuert, welche Ziffern auf der 7-Segment-Anzeige aktiviert werden.
3. **Timer-Logik:**
Ein Threading-Timer erhöht den Zähler alle 0,01 Sekunden und sorgt so für eine präzise Zeitmessung.
4. **Zustandsverwaltung:**
Das Spiel wechselt je nach Tasteneingabe zwischen verschiedenen Zuständen (Start, Stopp) und setzt den Zähler bei Bedarf zurück.
5. **Anzeigeaktualisierung:**
Die 7-Segment-Anzeige zeigt den Timerwert mit zwei Dezimalstellen an.
----------------------------------------------
**Fehlerbehebung**
1. **7-Segment-Anzeige zeigt keine Zahlen an**:
- **Ursache**: Falsche Verdrahtung oder falsche GPIO-Konfiguration.
- **Lösung**:
- Überprüfen Sie die Verbindungen zum 74HC595-Schieberegister und zur 7-Segment-Anzeige.
- Stellen Sie sicher, dass die Segmentcodes in ``number`` zur Konfiguration der Anzeige passen.
2. **Neigungsschalter reagiert nicht**:
- **Ursache**: Fehlerhafte Verdrahtung oder fehlende Entprellung.
- **Lösung**:
- Prüfen Sie, ob der Neigungsschalter mit GPIO 22 verbunden ist.
- Fügen Sie eine kurze Entprellverzögerung in der Funktion ``loop()`` hinzu:
.. code-block:: python
time.sleep(0.05)
3. **Zähler erhöht sich nicht**:
- **Ursache**: Die Timerfunktion startet nicht korrekt.
- **Lösung**:
- Überprüfen Sie die Funktion ``timer()`` und stellen Sie sicher, dass ``timer1.start()`` aufgerufen wird.
- Fügen Sie zur Fehlersuche eine Ausgabe des ``counter``-Wertes innerhalb der Funktion ``timer()`` hinzu.
4. **Anzeige zeigt falsche Ziffern**:
- **Ursache**: Die Segmentcodes in der Liste ``number`` stimmen nicht mit der Hardware überein.
- **Lösung**: Testen Sie einzelne Ziffern mit festen Segmentcodes, um die korrekte Zuordnung zu überprüfen.
----------------------------------------------
**Erweiterungsideen**
1. **Anpassbare Timer-Geschwindigkeit**: Fügen Sie eine Möglichkeit hinzu, die Timer-Schrittweite zu ändern (z. B. mit einem zweiten Taster, um zwischen 0,01-, 0,1- und 1-Sekunden-Schritten zu wechseln).
2. **Countdown-Modus**: Implementieren Sie einen Countdown-Modus, bei dem der Zähler bei einem festgelegten Wert startet und bis 0 herunterzählt.
3. **Pause- und Fortsetzen-Funktion**: Fügen Sie einen separaten Taster hinzu, um den Timer anzuhalten und wieder fortzusetzen, ohne ihn zurückzusetzen.
4. **Akustisches Feedback**: Verwenden Sie einen Summer, der bei bestimmten Meilensteinen Signaltöne ausgibt (z. B. bei Vielfachen von 10).
----------------------------------------------
**Fazit**
Die „10-Sekunden-Challenge“ zeigt, wie einfache Komponenten wie ein Neigungsschalter und eine 7-Segment-Anzeige zu einem unterhaltsamen interaktiven Spiel kombiniert werden können. Das Projekt demonstriert die Nutzung von GPIO-Steuerung, Timern und Zustandsverwaltung in Python und bietet eine lehrreiche sowie spannende Erfahrung. Fordern Sie sich selbst und Ihre Freunde heraus, um das perfekte Timing zu erreichen!